Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Tönne <lukas.toenne@gmail.com>2016-12-28 19:30:58 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2016-12-28 19:30:58 +0300
commit6ecab6dd8e48d564a2b43e0e81e79d079e8b4c77 (patch)
tree618e2d24eb34a05a81f726dd52eb2b7468e9296d /source/blender
parent605263177b8eea24c1449e4dbf0138175ec3dddf (diff)
Revert particle system and point cache removal in blender2.8 branch.
This reverts commit 5aa19be91263a249ffae75573e3b32f24269d890 and b4a721af694817fa921b119df83d33ede7d7fed0. Due to postponement of particle system rewrite it was decided to put particle code back into the 2.8 branch for the time being.
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/CMakeLists.txt2
-rw-r--r--source/blender/alembic/intern/abc_exporter.cc17
-rw-r--r--source/blender/alembic/intern/abc_hair.cc18
-rw-r--r--source/blender/alembic/intern/abc_hair.h10
-rw-r--r--source/blender/alembic/intern/abc_mesh.cc2
-rw-r--r--source/blender/alembic/intern/abc_points.cc10
-rw-r--r--source/blender/alembic/intern/abc_points.h6
-rw-r--r--source/blender/blenkernel/BKE_boids.h66
-rw-r--r--source/blender/blenkernel/BKE_cloth.h3
-rw-r--r--source/blender/blenkernel/BKE_context.h1
-rw-r--r--source/blender/blenkernel/BKE_dynamicpaint.h1
-rw-r--r--source/blender/blenkernel/BKE_effect.h10
-rw-r--r--source/blender/blenkernel/BKE_library.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h1
-rw-r--r--source/blender/blenkernel/BKE_modifier.h1
-rw-r--r--source/blender/blenkernel/BKE_object.h3
-rw-r--r--source/blender/blenkernel/BKE_particle.h481
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h348
-rw-r--r--source/blender/blenkernel/BKE_rigidbody.h1
-rw-r--r--source/blender/blenkernel/BKE_sca.h2
-rw-r--r--source/blender/blenkernel/BKE_softbody.h2
-rw-r--r--source/blender/blenkernel/BKE_texture.h3
-rw-r--r--source/blender/blenkernel/CMakeLists.txt9
-rw-r--r--source/blender/blenkernel/intern/anim.c2
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c10
-rw-r--r--source/blender/blenkernel/intern/boids.c1618
-rw-r--r--source/blender/blenkernel/intern/bpath.c35
-rw-r--r--source/blender/blenkernel/intern/cloth.c89
-rw-r--r--source/blender/blenkernel/intern/context.c1
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c200
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c345
-rw-r--r--source/blender/blenkernel/intern/effect.c155
-rw-r--r--source/blender/blenkernel/intern/fluidsim.c1
-rw-r--r--source/blender/blenkernel/intern/group.c1
-rw-r--r--source/blender/blenkernel/intern/idcode.c4
-rw-r--r--source/blender/blenkernel/intern/ipo.c72
-rw-r--r--source/blender/blenkernel/intern/library.c16
-rw-r--r--source/blender/blenkernel/intern/library_query.c68
-rw-r--r--source/blender/blenkernel/intern/library_remap.c4
-rw-r--r--source/blender/blenkernel/intern/modifier.c7
-rw-r--r--source/blender/blenkernel/intern/object.c190
-rw-r--r--source/blender/blenkernel/intern/object_deform.c9
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c332
-rw-r--r--source/blender/blenkernel/intern/object_update.c49
-rw-r--r--source/blender/blenkernel/intern/particle.c4304
-rw-r--r--source/blender/blenkernel/intern/particle_child.c739
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c1476
-rw-r--r--source/blender/blenkernel/intern/particle_system.c4362
-rw-r--r--source/blender/blenkernel/intern/pointcache.c4095
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c93
-rw-r--r--source/blender/blenkernel/intern/scene.c20
-rw-r--r--source/blender/blenkernel/intern/smoke.c308
-rw-r--r--source/blender/blenkernel/intern/softbody.c68
-rw-r--r--source/blender/blenkernel/intern/texture.c44
-rw-r--r--source/blender/blenloader/intern/readfile.c465
-rw-r--r--source/blender/blenloader/intern/versioning_250.c77
-rw-r--r--source/blender/blenloader/intern/versioning_260.c36
-rw-r--r--source/blender/blenloader/intern/versioning_270.c100
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c7
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c208
-rw-r--r--source/blender/blenloader/intern/writefile.c226
-rw-r--r--source/blender/blentranslation/BLT_translation.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc51
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc145
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc11
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc38
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c3
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c5
-rw-r--r--source/blender/editors/animation/anim_filter.c72
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_buttons.h1
-rw-r--r--source/blender/editors/include/ED_object.h2
-rw-r--r--source/blender/editors/include/ED_particle.h74
-rw-r--r--source/blender/editors/include/ED_physics.h5
-rw-r--r--source/blender/editors/interface/interface_icons.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c57
-rw-r--r--source/blender/editors/object/object_add.c19
-rw-r--r--source/blender/editors/object/object_edit.c22
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_modifier.c283
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c5
-rw-r--r--source/blender/editors/object/object_select.c41
-rw-r--r--source/blender/editors/physics/CMakeLists.txt4
-rw-r--r--source/blender/editors/physics/particle_boids.c371
-rw-r--r--source/blender/editors/physics/particle_edit.c4923
-rw-r--r--source/blender/editors/physics/particle_object.c1244
-rw-r--r--source/blender/editors/physics/physics_intern.h67
-rw-r--r--source/blender/editors/physics/physics_ops.c152
-rw-r--r--source/blender/editors/physics/physics_pointcache.c469
-rw-r--r--source/blender/editors/render/render_shading.c10
-rw-r--r--source/blender/editors/screen/screen_context.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c16
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c107
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c50
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c7
-rw-r--r--source/blender/editors/space_file/filesel.c2
-rw-r--r--source/blender/editors/space_info/info_stats.c34
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c1
-rw-r--r--source/blender/editors/space_nla/nla_channels.c3
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c6
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h2
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c16
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c9
-rw-r--r--source/blender/editors/space_time/space_time.c162
-rw-r--r--source/blender/editors/space_view3d/drawobject.c1206
-rw-r--r--source/blender/editors/space_view3d/drawvolume.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_draw_legacy.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c12
-rw-r--r--source/blender/editors/transform/transform.c2
-rw-r--r--source/blender/editors/transform/transform.h1
-rw-r--r--source/blender/editors/transform/transform_conversions.c203
-rw-r--r--source/blender/editors/transform/transform_generics.c9
-rw-r--r--source/blender/editors/transform/transform_manipulator.c28
-rw-r--r--source/blender/editors/transform/transform_orientations.c2
-rw-r--r--source/blender/editors/transform/transform_snap.c8
-rw-r--r--source/blender/editors/transform/transform_snap_object.c16
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/undo.c34
-rw-r--r--source/blender/gpu/GPU_material.h17
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c8
-rw-r--r--source/blender/gpu/intern/gpu_draw.c36
-rw-r--r--source/blender/gpu/intern/gpu_material.c27
-rw-r--r--source/blender/makesdna/DNA_ID.h5
-rw-r--r--source/blender/makesdna/DNA_boid_types.h225
-rw-r--r--source/blender/makesdna/DNA_dynamicpaint_types.h3
-rw-r--r--source/blender/makesdna/DNA_ipo_types.h39
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h42
-rw-r--r--source/blender/makesdna/DNA_object_fluidsim.h2
-rw-r--r--source/blender/makesdna/DNA_object_force.h109
-rw-r--r--source/blender/makesdna/DNA_object_types.h7
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h2
-rw-r--r--source/blender/makesdna/DNA_particle_types.h613
-rw-r--r--source/blender/makesdna/DNA_rigidbody_types.h3
-rw-r--r--source/blender/makesdna/DNA_scene_types.h36
-rw-r--r--source/blender/makesdna/DNA_smoke_types.h4
-rw-r--r--source/blender/makesdna/DNA_space_types.h2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesdna/intern/makesdna.c4
-rw-r--r--source/blender/makesrna/RNA_access.h24
-rw-r--r--source/blender/makesrna/RNA_enum_types.h1
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/makesrna.c2
-rw-r--r--source/blender/makesrna/intern/rna_ID.c12
-rw-r--r--source/blender/makesrna/intern/rna_action.c6
-rw-r--r--source/blender/makesrna/intern/rna_boid.c674
-rw-r--r--source/blender/makesrna/intern/rna_color.c8
-rw-r--r--source/blender/makesrna/intern/rna_context.c1
-rw-r--r--source/blender/makesrna/intern/rna_dynamicpaint.c63
-rw-r--r--source/blender/makesrna/intern/rna_fluidsim.c48
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_main.c7
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c46
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c124
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c39
-rw-r--r--source/blender/makesrna/intern/rna_object.c80
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c49
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c594
-rw-r--r--source/blender/makesrna/intern/rna_particle.c3573
-rw-r--r--source/blender/makesrna/intern/rna_rigidbody.c17
-rw-r--r--source/blender/makesrna/intern/rna_scene.c33
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c322
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c102
-rw-r--r--source/blender/makesrna/intern/rna_space.c19
-rw-r--r--source/blender/makesrna/intern/rna_texture.c45
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c4
-rw-r--r--source/blender/modifiers/CMakeLists.txt2
-rw-r--r--source/blender/modifiers/MOD_modifiertypes.h2
-rw-r--r--source/blender/modifiers/intern/MOD_build.c2
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c13
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c2
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c943
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c3
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c470
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c241
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c2
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c2
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c2
-rw-r--r--source/blender/modifiers/intern/MOD_util.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_particle_info.c7
-rw-r--r--source/blender/render/intern/source/convertblender.c1291
-rw-r--r--source/blender/render/intern/source/pipeline.c28
-rw-r--r--source/blender/render/intern/source/pointdensity.c268
-rw-r--r--source/blender/render/intern/source/renderdatabase.c32
-rw-r--r--source/blender/render/intern/source/shadeinput.c1
-rw-r--r--source/blender/render/intern/source/voxeldata.c19
-rw-r--r--source/blender/windowmanager/WM_types.h2
193 files changed, 40944 insertions, 353 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index d49ceb1636b..6f2b78e0845 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -29,6 +29,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_actuator_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_anim_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h
@@ -67,6 +68,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_object_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_outliner_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_packedFile_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_particle_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_property_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_rigidbody_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_scene_types.h
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
index 92fee170e29..ff8b0442ab6 100644
--- a/source/blender/alembic/intern/abc_exporter.cc
+++ b/source/blender/alembic/intern/abc_exporter.cc
@@ -55,6 +55,7 @@ extern "C" {
#include "BKE_idprop.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
}
@@ -501,6 +502,22 @@ void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
return;
}
+ ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
+
+ for (; psys; psys = psys->next) {
+ if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
+ continue;
+ }
+
+ if (psys->part->type == PART_HAIR) {
+ m_settings.export_child_hairs = true;
+ m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+ }
+ else if (psys->part->type == PART_EMITTER) {
+ m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+ }
+ }
+
switch(ob->type) {
case OB_MESH:
{
diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc
index f10cfbadda8..14bcf6731ea 100644
--- a/source/blender/alembic/intern/abc_hair.cc
+++ b/source/blender/alembic/intern/abc_hair.cc
@@ -37,6 +37,7 @@ extern "C" {
#include "BKE_DerivedMesh.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
}
using Alembic::Abc::P3fArraySamplePtr;
@@ -53,15 +54,13 @@ AbcHairWriter::AbcHairWriter(Scene *scene,
AbcTransformWriter *parent,
uint32_t time_sampling,
ExportSettings &settings,
- void *UNUSED(psys))
+ ParticleSystem *psys)
: AbcObjectWriter(scene, ob, time_sampling, settings, parent)
{
- m_psys = NULL; // = psys;
+ m_psys = psys;
-#if 0
OCurves curves(parent->alembicXform(), psys->name, m_time_sampling);
m_schema = curves.getSchema();
-#endif
}
void AbcHairWriter::do_write()
@@ -69,7 +68,7 @@ void AbcHairWriter::do_write()
if (!m_psys) {
return;
}
-#if 0
+
ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys);
if (!psmd->dm_final) {
@@ -117,17 +116,15 @@ void AbcHairWriter::do_write()
m_sample.setSelfBounds(bounds());
m_schema.set(m_sample);
-#endif
}
void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
- void *part,
+ ParticleSettings *part,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
std::vector<int32_t> &hvertices)
{
-#if 0
/* Get untransformed vertices, there's a xform under the hair. */
float inv_mat[4][4];
invert_m4_m4_safe(inv_mat, m_object->obmat);
@@ -228,17 +225,15 @@ void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
++path;
}
}
-#endif
}
void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
- void *part,
+ ParticleSettings *part,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
std::vector<int32_t> &hvertices)
{
-#if 0
/* Get untransformed vertices, there's a xform under the hair. */
float inv_mat[4][4];
invert_m4_m4_safe(inv_mat, m_object->obmat);
@@ -292,5 +287,4 @@ void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
++path;
}
}
-#endif
}
diff --git a/source/blender/alembic/intern/abc_hair.h b/source/blender/alembic/intern/abc_hair.h
index bbd5f2c4361..d132b60be12 100644
--- a/source/blender/alembic/intern/abc_hair.h
+++ b/source/blender/alembic/intern/abc_hair.h
@@ -26,11 +26,13 @@
#include "abc_object.h"
struct DerivedMesh;
+struct ParticleSettings;
+struct ParticleSystem;
/* ************************************************************************** */
class AbcHairWriter : public AbcObjectWriter {
- /*ParticleSystem*/ void *m_psys;
+ ParticleSystem *m_psys;
Alembic::AbcGeom::OCurvesSchema m_schema;
Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
@@ -41,20 +43,20 @@ public:
AbcTransformWriter *parent,
uint32_t time_sampling,
ExportSettings &settings,
- /*ParticleSystem*/void *psys);
+ ParticleSystem *psys);
private:
virtual void do_write();
void write_hair_sample(DerivedMesh *dm,
- /*ParticleSettings*/ void *part,
+ ParticleSettings *part,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
std::vector<int32_t> &hvertices);
void write_hair_child_sample(DerivedMesh *dm,
- /*ParticleSettings*/ void *part,
+ ParticleSettings *part,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
index ad0d0a430c1..bdd75f93189 100644
--- a/source/blender/alembic/intern/abc_mesh.cc
+++ b/source/blender/alembic/intern/abc_mesh.cc
@@ -262,7 +262,7 @@ static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
}
/* mesh is not a subsurf. break */
- if ((md->type != eModifierType_Displace) /*&& (md->type != eModifierType_ParticleSystem)*/) {
+ if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
return NULL;
}
}
diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc
index 6602c7e5a85..4c78f3e83c7 100644
--- a/source/blender/alembic/intern/abc_points.cc
+++ b/source/blender/alembic/intern/abc_points.cc
@@ -36,6 +36,7 @@ extern "C" {
#include "BKE_lattice.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BLI_math.h"
@@ -62,15 +63,13 @@ AbcPointsWriter::AbcPointsWriter(Scene *scene,
AbcTransformWriter *parent,
uint32_t time_sampling,
ExportSettings &settings,
- void *UNUSED(psys))
+ ParticleSystem *psys)
: AbcObjectWriter(scene, ob, time_sampling, settings, parent)
{
- m_psys = NULL; // = psys;
+ m_psys = psys;
-#if 0
OPoints points(parent->alembicXform(), psys->name, m_time_sampling);
m_schema = points.getSchema();
-#endif
}
void AbcPointsWriter::do_write()
@@ -78,7 +77,7 @@ void AbcPointsWriter::do_write()
if (!m_psys) {
return;
}
-#if 0
+
std::vector<Imath::V3f> points;
std::vector<Imath::V3f> velocities;
std::vector<float> widths;
@@ -135,7 +134,6 @@ void AbcPointsWriter::do_write()
m_sample.setSelfBounds(bounds());
m_schema.set(m_sample);
-#endif
}
/* ************************************************************************** */
diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h
index 9864917f477..cb68dbca4d5 100644
--- a/source/blender/alembic/intern/abc_points.h
+++ b/source/blender/alembic/intern/abc_points.h
@@ -28,12 +28,14 @@
#include "abc_object.h"
#include "abc_customdata.h"
+struct ParticleSystem;
+
/* ************************************************************************** */
class AbcPointsWriter : public AbcObjectWriter {
Alembic::AbcGeom::OPointsSchema m_schema;
Alembic::AbcGeom::OPointsSchema::Sample m_sample;
- /*ParticleSystem*/ void *m_psys;
+ ParticleSystem *m_psys;
public:
AbcPointsWriter(Scene *scene,
@@ -41,7 +43,7 @@ public:
AbcTransformWriter *parent,
uint32_t time_sampling,
ExportSettings &settings,
- /*ParticleSystem*/ void *psys);
+ ParticleSystem *psys);
void do_write();
};
diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h
new file mode 100644
index 00000000000..582cd0cef8d
--- /dev/null
+++ b/source/blender/blenkernel/BKE_boids.h
@@ -0,0 +1,66 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_BOIDS_H__
+#define __BKE_BOIDS_H__
+
+/** \file BKE_boids.h
+ * \ingroup bke
+ * \since 2009
+ * \author Janne Karhu
+ */
+
+#include "DNA_boid_types.h"
+
+struct RNG;
+
+typedef struct BoidBrainData {
+ struct ParticleSimulationData *sim;
+ struct ParticleSettings *part;
+ float timestep, cfra, dfra;
+ float wanted_co[3], wanted_speed;
+
+ /* Goal stuff */
+ struct Object *goal_ob;
+ float goal_co[3];
+ float goal_nor[3];
+ float goal_priority;
+
+ struct RNG *rng;
+} BoidBrainData;
+
+void boids_precalc_rules(struct ParticleSettings *part, float cfra);
+void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa);
+void boid_body(BoidBrainData *bbd, struct ParticleData *pa);
+void boid_default_settings(BoidSettings *boids);
+BoidRule *boid_new_rule(int type);
+BoidState *boid_new_state(BoidSettings *boids);
+BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state);
+void boid_free_settings(BoidSettings *boids);
+BoidSettings *boid_copy_settings(BoidSettings *boids);
+BoidState *boid_get_current_state(BoidSettings *boids);
+#endif
diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index 56d9b2439fb..36330242f18 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -237,6 +237,9 @@ int cloth_uses_vgroup(struct ClothModifierData *clmd);
void bvhtree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
void bvhselftree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
+// needed for button_object.c
+void cloth_clear_cache (struct Object *ob, struct ClothModifierData *clmd, float framenr );
+
// needed for cloth.c
int cloth_add_spring (struct ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type);
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index c6795f87eab..4da6a61cbfa 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -109,6 +109,7 @@ enum {
CTX_MODE_PAINT_WEIGHT,
CTX_MODE_PAINT_VERTEX,
CTX_MODE_PAINT_TEXTURE,
+ CTX_MODE_PARTICLE,
CTX_MODE_OBJECT
};
diff --git a/source/blender/blenkernel/BKE_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h
index c552a618cd8..5abb53d4c52 100644
--- a/source/blender/blenkernel/BKE_dynamicpaint.h
+++ b/source/blender/blenkernel/BKE_dynamicpaint.h
@@ -73,6 +73,7 @@ void dynamicPaint_freeCanvas(struct DynamicPaintModifierData *pmd);
void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd);
void dynamicPaint_freeSurfaceData(struct DynamicPaintSurface *surface);
+void dynamicPaint_cacheUpdateFrames(struct DynamicPaintSurface *surface);
bool dynamicPaint_surfaceHasColorPreview(struct DynamicPaintSurface *surface);
bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, struct Object *ob, int output);
void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface);
diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h
index 0645bc1e00e..aa45132cbe9 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -41,7 +41,9 @@ struct Object;
struct Scene;
struct ListBase;
struct Group;
-struct PointCacheKey;
+struct ParticleSimulationData;
+struct ParticleData;
+struct ParticleKey;
struct EffectorWeights *BKE_add_effector_weights(struct Group *group);
struct PartDeflect *object_add_collision_fields(int type);
@@ -93,6 +95,7 @@ typedef struct EffectorCache {
struct Scene *scene;
struct Object *ob;
+ struct ParticleSystem *psys;
struct SurfaceModifierData *surmd;
struct PartDeflect *pd;
@@ -107,14 +110,17 @@ typedef struct EffectorCache {
} EffectorCache;
void free_partdeflect(struct PartDeflect *pd);
-struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct EffectorWeights *weights, bool for_simulation);
+struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool for_simulation);
void pdEndEffectors(struct ListBase **effectors);
void pdPrecalculateEffectors(struct ListBase *effectors);
void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse);
+void pd_point_from_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleKey *state, struct EffectedPoint *point);
void pd_point_from_loc(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
void pd_point_from_soft(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point);
+/* needed for boids */
+float effector_falloff(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, struct EffectorWeights *weights);
int closest_point_on_surface(SurfaceModifierData *surmd, const float co[3], float surface_co[3], float surface_nor[3], float surface_vel[3]);
int get_effector_data(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, int real_velocity);
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index d3f98f2989a..2d9c35f7fd0 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -95,7 +95,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma
struct ListBase *which_libbase(struct Main *mainlib, short type);
-#define MAX_LIBARRAY 34
+#define MAX_LIBARRAY 35
int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
/* Main API */
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 7eba01e6d5a..a4f5c425282 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -96,6 +96,7 @@ typedef struct Main {
ListBase action;
ListBase nodetree;
ListBase brush;
+ ListBase particle;
ListBase palettes;
ListBase paintcurves;
ListBase wm;
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index ab0b120faf3..f6c08909d23 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -374,6 +374,7 @@ int modifiers_getCageIndex(struct Scene *scene, struct Object *ob,
bool modifiers_isModifierEnabled(struct Object *ob, int modifierType);
bool modifiers_isSoftbodyEnabled(struct Object *ob);
bool modifiers_isClothEnabled(struct Object *ob);
+bool modifiers_isParticleEnabled(struct Object *ob);
struct Object *modifiers_isDeformedByArmature(struct Object *ob);
struct Object *modifiers_isDeformedByLattice(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 29e84336526..cf07a178fe8 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -55,7 +55,10 @@ void BKE_object_workob_calc_parent(struct Scene *scene, struct Object *ob, struc
void BKE_object_transform_copy(struct Object *ob_tar, const struct Object *ob_src);
struct SoftBody *copy_softbody(const struct SoftBody *sb, bool copy_caches);
struct BulletSoftBody *copy_bulletsoftbody(struct BulletSoftBody *sb);
+struct ParticleSystem *BKE_object_copy_particlesystem(struct ParticleSystem *psys);
+void BKE_object_copy_particlesystems(struct Object *ob_dst, const struct Object *ob_src);
void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src);
+void BKE_object_free_particlesystems(struct Object *ob);
void BKE_object_free_softbody(struct Object *ob);
void BKE_object_free_bulletsoftbody(struct Object *ob);
void BKE_object_free_curve_cache(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
new file mode 100644
index 00000000000..b3e3968ca9b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -0,0 +1,481 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Adaptive time step
+ * Classical SPH
+ * Copyright 2011-2012 AutoCRC
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_PARTICLE_H__
+#define __BKE_PARTICLE_H__
+
+/** \file BKE_particle.h
+ * \ingroup bke
+ */
+
+#include "BLI_utildefines.h"
+
+#include "DNA_particle_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_customdata.h"
+
+struct ParticleSystemModifierData;
+struct ParticleSystem;
+struct ParticleKey;
+struct ParticleSettings;
+
+struct Main;
+struct Object;
+struct Scene;
+struct DerivedMesh;
+struct ModifierData;
+struct MTFace;
+struct MCol;
+struct MFace;
+struct MVert;
+struct LatticeDeformData;
+struct LinkNode;
+struct KDTree;
+struct RNG;
+struct BVHTreeRay;
+struct BVHTreeRayHit;
+struct EdgeHash;
+
+#define PARTICLE_COLLISION_MAX_COLLISIONS 10
+
+#define PARTICLE_P ParticleData * pa; int p
+#define LOOP_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++)
+#define LOOP_EXISTING_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & PARS_UNEXIST))
+#define LOOP_SHOWN_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & (PARS_UNEXIST | PARS_NO_DISP)))
+/* OpenMP: Can only advance one variable within loop definition. */
+#define LOOP_DYNAMIC_PARTICLES for (p = 0; p < psys->totpart; p++) if ((pa = psys->particles + p)->state.time > 0.0f)
+
+/* fast but sure way to get the modifier*/
+#define PARTICLE_PSMD ParticleSystemModifierData * psmd = sim->psmd ? sim->psmd : psys_get_modifier(sim->ob, sim->psys)
+
+/* common stuff that many particle functions need */
+typedef struct ParticleSimulationData {
+ struct Scene *scene;
+ struct Object *ob;
+ struct ParticleSystem *psys;
+ struct ParticleSystemModifierData *psmd;
+ struct ListBase *colliders;
+ /* Courant number. This is used to implement an adaptive time step. Only the
+ * maximum value per time step is important. Only sph_integrate makes use of
+ * this at the moment. Other solvers could, too. */
+ float courant_num;
+} ParticleSimulationData;
+
+typedef struct SPHData {
+ ParticleSystem *psys[10];
+ ParticleData *pa;
+ float mass;
+ struct EdgeHash *eh;
+ float *gravity;
+ float hfac;
+ /* Average distance to neighbours (other particles in the support domain),
+ * for calculating the Courant number (adaptive time step). */
+ int pass;
+ float element_size;
+ float flow[3];
+
+ /* Integrator callbacks. This allows different SPH implementations. */
+ void (*force_cb) (void *sphdata_v, ParticleKey *state, float *force, float *impulse);
+ void (*density_cb) (void *rangedata_v, int index, const float co[3], float squared_dist);
+} SPHData;
+
+typedef struct ParticleTexture {
+ float ivel; /* used in reset */
+ float time, life, exist, size; /* used in init */
+ float damp, gravity, field; /* used in physics */
+ float length, clump, kink_freq, kink_amp, effector; /* used in path caching */
+ float rough1, rough2, roughe; /* used in path caching */
+} ParticleTexture;
+
+typedef struct ParticleSeam {
+ float v0[3], v1[3];
+ float nor[3], dir[3], tan[3];
+ float length2;
+} ParticleSeam;
+
+typedef struct ParticleCacheKey {
+ float co[3];
+ float vel[3];
+ float rot[4];
+ float col[3];
+ float time;
+ int segments;
+} ParticleCacheKey;
+
+typedef struct ParticleThreadContext {
+ /* shared */
+ struct ParticleSimulationData sim;
+ struct DerivedMesh *dm;
+ struct Material *ma;
+
+ /* distribution */
+ struct KDTree *tree;
+
+ struct ParticleSeam *seams;
+ int totseam;
+
+ float *jit, *jitoff, *weight;
+ float maxweight;
+ int *index, *skip, jitlevel;
+
+ int cfrom, distr;
+
+ struct ParticleData *tpars;
+
+ /* path caching */
+ bool editupdate;
+ int between, segments, extra_segments;
+ int totchild, totparent, parent_pass;
+
+ float cfra;
+
+ float *vg_length, *vg_clump, *vg_kink;
+ float *vg_rough1, *vg_rough2, *vg_roughe;
+ float *vg_effector;
+
+ struct CurveMapping *clumpcurve;
+ struct CurveMapping *roughcurve;
+} ParticleThreadContext;
+
+typedef struct ParticleTask {
+ ParticleThreadContext *ctx;
+ struct RNG *rng, *rng_path;
+ int begin, end;
+} ParticleTask;
+
+typedef struct ParticleBillboardData {
+ struct Object *ob;
+ float vec[3], vel[3];
+ float offset[2];
+ float size[2];
+ float tilt, random, time;
+ int uv[3];
+ int lock, num;
+ int totnum;
+ int lifetime;
+ short align, uv_split, anim, split_offset;
+} ParticleBillboardData;
+
+typedef struct ParticleCollisionElement {
+ /* pointers to original data */
+ float *x[3], *v[3];
+
+ /* values interpolated from original data*/
+ float x0[3], x1[3], x2[3], p[3];
+
+ /* results for found intersection point */
+ float nor[3], vel[3], uv[2];
+
+ /* count of original data (1-4) */
+ int tot;
+
+ /* index of the collision face */
+ int index;
+
+ /* flags for inversed normal / particle already inside element at start */
+ short inv_nor, inside;
+} ParticleCollisionElement;
+
+/* container for moving data between deflet_particle and particle_intersect_face */
+typedef struct ParticleCollision {
+ struct Object *current;
+ struct Object *hit;
+ struct Object *skip[PARTICLE_COLLISION_MAX_COLLISIONS+1];
+ struct Object *emitter;
+
+ struct CollisionModifierData *md; // collision modifier for current object;
+
+ float f; // time factor of previous collision, needed for substracting face velocity
+ float fac1, fac2;
+
+ float cfra, old_cfra;
+
+ float original_ray_length; //original length of co2-co1, needed for collision time evaluation
+
+ int skip_count;
+
+ ParticleCollisionElement pce;
+
+ /* total_time is the amount of time in this subframe
+ * inv_total_time is the opposite
+ * inv_timestep is the inverse of the amount of time in this frame */
+ float total_time, inv_total_time, inv_timestep;
+
+ float radius;
+ float co1[3], co2[3];
+ float ve1[3], ve2[3];
+
+ float acc[3], boid_z;
+
+ int boid;
+} ParticleCollision;
+
+typedef struct ParticleDrawData {
+ float *vdata, *vd; /* vertice data */
+ float *ndata, *nd; /* normal data */
+ float *cdata, *cd; /* color data */
+ float *vedata, *ved; /* velocity data */
+ float *ma_col;
+ int tot_vec_size, flag;
+ int totpoint, totve;
+} ParticleDrawData;
+
+#define PARTICLE_DRAW_DATA_UPDATED 1
+
+#define PSYS_FRAND_COUNT 1024
+extern unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT];
+extern unsigned int PSYS_FRAND_SEED_MULTIPLIER[PSYS_FRAND_COUNT];
+extern float PSYS_FRAND_BASE[PSYS_FRAND_COUNT];
+
+void psys_init_rng(void);
+
+BLI_INLINE float psys_frand(ParticleSystem *psys, unsigned int seed)
+{
+ /* XXX far from ideal, this simply scrambles particle random numbers a bit
+ * to avoid obvious correlations.
+ * Can't use previous psys->frand arrays because these require initialization
+ * inside psys_check_enabled, which wreaks havok in multithreaded depgraph updates.
+ */
+ unsigned int offset = PSYS_FRAND_SEED_OFFSET[psys->seed % PSYS_FRAND_COUNT];
+ unsigned int multiplier = PSYS_FRAND_SEED_MULTIPLIER[psys->seed % PSYS_FRAND_COUNT];
+ return PSYS_FRAND_BASE[(offset + seed * multiplier) % PSYS_FRAND_COUNT];
+}
+
+BLI_INLINE void psys_frand_vec(ParticleSystem *psys, unsigned int seed, float vec[3])
+{
+ unsigned int offset = PSYS_FRAND_SEED_OFFSET[psys->seed % PSYS_FRAND_COUNT];
+ unsigned int multiplier = PSYS_FRAND_SEED_MULTIPLIER[psys->seed % PSYS_FRAND_COUNT];
+ vec[0] = PSYS_FRAND_BASE[(offset + (seed + 0) * multiplier) % PSYS_FRAND_COUNT];
+ vec[1] = PSYS_FRAND_BASE[(offset + (seed + 1) * multiplier) % PSYS_FRAND_COUNT];
+ vec[2] = PSYS_FRAND_BASE[(offset + (seed + 2) * multiplier) % PSYS_FRAND_COUNT];
+}
+
+/* ----------- functions needed outside particlesystem ---------------- */
+/* particle.c */
+int count_particles(struct ParticleSystem *psys);
+int count_particles_mod(struct ParticleSystem *psys, int totgr, int cur);
+
+int psys_get_child_number(struct Scene *scene, struct ParticleSystem *psys);
+int psys_get_tot_child(struct Scene *scene, struct ParticleSystem *psys);
+
+struct ParticleSystem *psys_get_current(struct Object *ob);
+/* for rna */
+short psys_get_current_num(struct Object *ob);
+void psys_set_current_num(Object *ob, int index);
+/* UNUSED */
+// struct Object *psys_find_object(struct Scene *scene, struct ParticleSystem *psys);
+
+struct LatticeDeformData *psys_create_lattice_deform_data(struct ParticleSimulationData *sim);
+
+bool psys_in_edit_mode(struct Scene *scene, struct ParticleSystem *psys);
+bool psys_check_enabled(struct Object *ob, struct ParticleSystem *psys, const bool use_render_params);
+bool psys_check_edited(struct ParticleSystem *psys);
+
+void psys_check_group_weights(struct ParticleSettings *part);
+int psys_uses_gravity(struct ParticleSimulationData *sim);
+
+/* free */
+void BKE_particlesettings_free(struct ParticleSettings *part);
+void psys_free_path_cache(struct ParticleSystem *psys, struct PTCacheEdit *edit);
+void psys_free(struct Object *ob, struct ParticleSystem *psys);
+
+void psys_render_set(struct Object *ob, struct ParticleSystem *psys, float viewmat[4][4], float winmat[4][4], int winx, int winy, int timeoffset);
+void psys_render_restore(struct Object *ob, struct ParticleSystem *psys);
+bool psys_render_simplify_params(struct ParticleSystem *psys, struct ChildParticle *cpa, float *params);
+
+void psys_interpolate_uvs(const struct MTFace *tface, int quad, const float w[4], float uvco[2]);
+void psys_interpolate_mcol(const struct MCol *mcol, int quad, const float w[4], struct MCol *mc);
+
+void copy_particle_key(struct ParticleKey *to, struct ParticleKey *from, int time);
+
+CustomDataMask psys_emitter_customdata_mask(struct ParticleSystem *psys);
+void psys_particle_on_emitter(struct ParticleSystemModifierData *psmd, int distr, int index, int index_dmcache,
+ float fuv[4], float foffset, float vec[3], float nor[3],
+ float utan[3], float vtan[3], float orco[3], float ornor[3]);
+struct ParticleSystemModifierData *psys_get_modifier(struct Object *ob, struct ParticleSystem *psys);
+
+struct ModifierData *object_add_particle_system(struct Scene *scene, struct Object *ob, const char *name);
+void object_remove_particle_system(struct Scene *scene, struct Object *ob);
+struct ParticleSettings *psys_new_settings(const char *name, struct Main *main);
+struct ParticleSettings *BKE_particlesettings_copy(struct Main *bmain, struct ParticleSettings *part);
+void BKE_particlesettings_make_local(struct Main *bmain, struct ParticleSettings *part, const bool lib_local);
+
+void psys_reset(struct ParticleSystem *psys, int mode);
+
+void psys_find_parents(struct ParticleSimulationData *sim, const bool use_render_params);
+
+void psys_cache_paths(struct ParticleSimulationData *sim, float cfra, const bool use_render_params);
+void psys_cache_edit_paths(struct Scene *scene, struct Object *ob, struct PTCacheEdit *edit, float cfra, const bool use_render_params);
+void psys_cache_child_paths(struct ParticleSimulationData *sim, float cfra, const bool editupdate, const bool use_render_params);
+int do_guides(struct ParticleSettings *part, struct ListBase *effectors, ParticleKey *state, int pa_num, float time);
+void precalc_guides(struct ParticleSimulationData *sim, struct ListBase *effectors);
+float psys_get_timestep(struct ParticleSimulationData *sim);
+float psys_get_child_time(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *birthtime, float *dietime);
+float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *pa_time);
+void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, struct ParticleKey *state, const bool vel);
+int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always);
+
+/* child paths */
+void BKE_particlesettings_clump_curve_init(struct ParticleSettings *part);
+void BKE_particlesettings_rough_curve_init(struct ParticleSettings *part);
+void psys_apply_child_modifiers(struct ParticleThreadContext *ctx, struct ListBase *modifiers,
+ struct ChildParticle *cpa, struct ParticleTexture *ptex, const float orco[3], const float ornor[3], float hairmat[4][4],
+ struct ParticleCacheKey *keys, struct ParticleCacheKey *parent_keys, const float parent_orco[3]);
+
+void psys_sph_init(struct ParticleSimulationData *sim, struct SPHData *sphdata);
+void psys_sph_finalise(struct SPHData *sphdata);
+void psys_sph_density(struct BVHTree *tree, struct SPHData *data, float co[3], float vars[2]);
+
+/* for anim.c */
+void psys_get_dupli_texture(struct ParticleSystem *psys, struct ParticleSettings *part,
+ struct ParticleSystemModifierData *psmd, struct ParticleData *pa, struct ChildParticle *cpa,
+ float uv[2], float orco[3]);
+void psys_get_dupli_path_transform(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ChildParticle *cpa,
+ struct ParticleCacheKey *cache, float mat[4][4], float *scale);
+
+void psys_thread_context_init(struct ParticleThreadContext *ctx, struct ParticleSimulationData *sim);
+void psys_thread_context_free(struct ParticleThreadContext *ctx);
+void psys_tasks_create(struct ParticleThreadContext *ctx, int startpart, int endpart, struct ParticleTask **r_tasks, int *r_numtasks);
+void psys_tasks_free(struct ParticleTask *tasks, int numtasks);
+
+void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]);
+void psys_apply_hair_lattice(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys);
+
+/* particle_system.c */
+struct ParticleSystem *psys_get_target_system(struct Object *ob, struct ParticleTarget *pt);
+void psys_count_keyed_targets(struct ParticleSimulationData *sim);
+void psys_update_particle_tree(struct ParticleSystem *psys, float cfra);
+void psys_changed_type(struct Object *ob, struct ParticleSystem *psys);
+
+void psys_make_temp_pointcache(struct Object *ob, struct ParticleSystem *psys);
+void psys_get_pointcache_start_end(struct Scene *scene, ParticleSystem *psys, int *sfra, int *efra);
+
+void psys_check_boid_data(struct ParticleSystem *psys);
+
+void psys_get_birth_coords(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleKey *state, float dtime, float cfra);
+
+void particle_system_update(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, const bool use_render_params);
+
+/* Callback format for performing operations on ID-pointers for particle systems */
+typedef void (*ParticleSystemIDFunc)(struct ParticleSystem *psys, struct ID **idpoin, void *userdata, int cd_flag);
+
+void BKE_particlesystem_id_loop(struct ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata);
+
+/* ----------- functions needed only inside particlesystem ------------ */
+/* particle.c */
+void psys_disable_all(struct Object *ob);
+void psys_enable_all(struct Object *ob);
+
+void free_hair(struct Object *ob, struct ParticleSystem *psys, int dynamics);
+void free_keyed_keys(struct ParticleSystem *psys);
+void psys_free_particles(struct ParticleSystem *psys);
+void psys_free_children(struct ParticleSystem *psys);
+
+void psys_interpolate_particle(short type, struct ParticleKey keys[4], float dt, struct ParticleKey *result, bool velocity);
+void psys_vec_rot_to_face(struct DerivedMesh *dm, struct ParticleData *pa, float vec[3]);
+void psys_mat_hair_to_object(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+void psys_mat_hair_to_global(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+void psys_mat_hair_to_orco(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+
+float psys_get_dietime_from_cache(struct PointCache *cache, int index);
+
+void psys_free_pdd(struct ParticleSystem *psys);
+
+float *psys_cache_vgroup(struct DerivedMesh *dm, struct ParticleSystem *psys, int vgroup);
+void psys_get_texture(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleTexture *ptex, int event, float cfra);
+void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFace *tface,
+ float (*orcodata)[3], float w[4], float vec[3], float nor[3], float utan[3], float vtan[3],
+ float orco[3], float ornor[3]);
+float psys_particle_value_from_verts(struct DerivedMesh *dm, short from, struct ParticleData *pa, float *values);
+void psys_get_from_key(struct ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time);
+
+/* BLI_bvhtree_ray_cast callback */
+void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
+void psys_particle_on_dm(struct DerivedMesh *dm_final, int from, int index, int index_dmcache,
+ const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
+ float orco[3], float ornor[3]);
+
+/* particle_system.c */
+void distribute_particles(struct ParticleSimulationData *sim, int from);
+void initialize_particle(struct ParticleSimulationData *sim, struct ParticleData *pa);
+void psys_calc_dmcache(struct Object *ob, struct DerivedMesh *dm_final, struct DerivedMesh *dm_deformed, struct ParticleSystem *psys);
+int psys_particle_dm_face_lookup(struct DerivedMesh *dm_final, struct DerivedMesh *dm_deformed, int findex, const float fw[4], struct LinkNode **poly_nodes);
+
+void reset_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, float dtime, float cfra);
+
+float psys_get_current_display_percentage(struct ParticleSystem *psys);
+
+typedef struct ParticleRenderElem {
+ int curchild, totchild, reduce;
+ float lambda, t, scalemin, scalemax;
+} ParticleRenderElem;
+
+typedef struct ParticleRenderData {
+ ChildParticle *child;
+ ParticleCacheKey **pathcache;
+ ParticleCacheKey **childcache;
+ ListBase pathcachebufs, childcachebufs;
+ int totchild, totcached, totchildcache;
+ struct DerivedMesh *dm;
+ int totdmvert, totdmedge, totdmface;
+
+ float mat[4][4];
+ float viewmat[4][4], winmat[4][4];
+ int winx, winy;
+
+ int do_simplify;
+ int timeoffset;
+ ParticleRenderElem *elems;
+
+ /* ORIGINDEX */
+ const int *index_mf_to_mpoly;
+ const int *index_mp_to_orig;
+} ParticleRenderData;
+
+/* psys_reset */
+#define PSYS_RESET_ALL 1
+#define PSYS_RESET_DEPSGRAPH 2
+/* #define PSYS_RESET_CHILDREN 3 */ /*UNUSED*/
+#define PSYS_RESET_CACHE_MISS 4
+
+/* index_dmcache */
+#define DMCACHE_NOTFOUND -1
+#define DMCACHE_ISCHILD -2
+
+/* **** Depsgraph evaluation **** */
+
+struct EvaluationContext;
+
+void BKE_particle_system_eval(struct EvaluationContext *eval_ctx,
+ struct Scene *scene,
+ struct Object *ob,
+ struct ParticleSystem *psys);
+
+#endif
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
new file mode 100644
index 00000000000..02f6c435ee2
--- /dev/null
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -0,0 +1,348 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2006 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Campbell Barton <ideasman42@gmail.com>
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_POINTCACHE_H__
+#define __BKE_POINTCACHE_H__
+
+/** \file BKE_pointcache.h
+ * \ingroup bke
+ */
+
+#include "DNA_ID.h"
+#include "DNA_dynamicpaint_types.h"
+#include "DNA_object_force.h"
+#include "DNA_boid_types.h"
+#include <stdio.h> /* for FILE */
+
+/* Point cache clearing option, for BKE_ptcache_id_clear, before
+ * and after are non inclusive (they wont remove the cfra) */
+#define PTCACHE_CLEAR_ALL 0
+#define PTCACHE_CLEAR_FRAME 1
+#define PTCACHE_CLEAR_BEFORE 2
+#define PTCACHE_CLEAR_AFTER 3
+
+/* Point cache reset options */
+#define PTCACHE_RESET_DEPSGRAPH 0
+#define PTCACHE_RESET_BAKED 1
+#define PTCACHE_RESET_OUTDATED 2
+/* #define PTCACHE_RESET_FREE 3 */ /*UNUSED*/
+
+/* Add the blendfile name after blendcache_ */
+#define PTCACHE_EXT ".bphys"
+#define PTCACHE_PATH "blendcache_"
+
+/* File open options, for BKE_ptcache_file_open */
+#define PTCACHE_FILE_READ 0
+#define PTCACHE_FILE_WRITE 1
+#define PTCACHE_FILE_UPDATE 2
+
+/* PTCacheID types */
+#define PTCACHE_TYPE_SOFTBODY 0
+#define PTCACHE_TYPE_PARTICLES 1
+#define PTCACHE_TYPE_CLOTH 2
+#define PTCACHE_TYPE_SMOKE_DOMAIN 3
+#define PTCACHE_TYPE_SMOKE_HIGHRES 4
+#define PTCACHE_TYPE_DYNAMICPAINT 5
+#define PTCACHE_TYPE_RIGIDBODY 6
+
+/* high bits reserved for flags that need to be stored in file */
+#define PTCACHE_TYPEFLAG_COMPRESS (1 << 16)
+#define PTCACHE_TYPEFLAG_EXTRADATA (1 << 17)
+
+#define PTCACHE_TYPEFLAG_TYPEMASK 0x0000FFFF
+#define PTCACHE_TYPEFLAG_FLAGMASK 0xFFFF0000
+
+/* PTCache read return code */
+#define PTCACHE_READ_EXACT 1
+#define PTCACHE_READ_INTERPOLATED 2
+#define PTCACHE_READ_OLD 3
+
+/* Structs */
+struct ClothModifierData;
+struct ListBase;
+struct Main;
+struct Object;
+struct ParticleKey;
+struct ParticleSystem;
+struct PointCache;
+struct Scene;
+struct SmokeModifierData;
+struct SoftBody;
+struct RigidBodyWorld;
+
+struct OpenVDBReader;
+struct OpenVDBWriter;
+
+/* temp structure for read/write */
+typedef struct PTCacheData {
+ unsigned int index;
+ float loc[3];
+ float vel[3];
+ float rot[4];
+ float ave[3];
+ float size;
+ float times[3];
+ struct BoidData boids;
+} PTCacheData;
+
+typedef struct PTCacheFile {
+ FILE *fp;
+
+ int frame, old_format;
+ unsigned int totpoint, type;
+ unsigned int data_types, flag;
+
+ struct PTCacheData data;
+ void *cur[BPHYS_TOT_DATA];
+} PTCacheFile;
+
+#define PTCACHE_VEL_PER_SEC 1
+
+enum {
+ PTCACHE_FILE_PTCACHE = 0,
+ PTCACHE_FILE_OPENVDB = 1,
+};
+
+typedef struct PTCacheID {
+ struct PTCacheID *next, *prev;
+
+ struct Scene *scene;
+ struct Object *ob;
+ void *calldata;
+ unsigned int type, file_type;
+ unsigned int stack_index;
+ unsigned int flag;
+
+ unsigned int default_step;
+ unsigned int max_step;
+
+ /* flags defined in DNA_object_force.h */
+ unsigned int data_types, info_types;
+
+ /* copies point data to cache data */
+ int (*write_point)(int index, void *calldata, void **data, int cfra);
+ /* copies cache cata to point data */
+ void (*read_point)(int index, void *calldata, void **data, float cfra, float *old_data);
+ /* interpolated between previously read point data and cache data */
+ void (*interpolate_point)(int index, void *calldata, void **data, float cfra, float cfra1, float cfra2, float *old_data);
+
+ /* copies point data to cache data */
+ int (*write_stream)(PTCacheFile *pf, void *calldata);
+ /* copies cache cata to point data */
+ int (*read_stream)(PTCacheFile *pf, void *calldata);
+
+ /* copies point data to cache data */
+ int (*write_openvdb_stream)(struct OpenVDBWriter *writer, void *calldata);
+ /* copies cache cata to point data */
+ int (*read_openvdb_stream)(struct OpenVDBReader *reader, void *calldata);
+
+ /* copies custom extradata to cache data */
+ void (*write_extra_data)(void *calldata, struct PTCacheMem *pm, int cfra);
+ /* copies custom extradata to cache data */
+ void (*read_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra);
+ /* copies custom extradata to cache data */
+ void (*interpolate_extra_data)(void *calldata, struct PTCacheMem *pm, float cfra, float cfra1, float cfra2);
+
+ /* total number of simulated points (the cfra parameter is just for using same function pointer with totwrite) */
+ int (*totpoint)(void *calldata, int cfra);
+ /* report error if number of points does not match */
+ void (*error)(void *calldata, const char *message);
+ /* number of points written for current cache frame */
+ int (*totwrite)(void *calldata, int cfra);
+
+ int (*write_header)(PTCacheFile *pf);
+ int (*read_header)(PTCacheFile *pf);
+
+ struct PointCache *cache;
+ /* used for setting the current cache from ptcaches list */
+ struct PointCache **cache_ptr;
+ struct ListBase *ptcaches;
+} PTCacheID;
+
+typedef struct PTCacheBaker {
+ struct Main *main;
+ struct Scene *scene;
+ int bake;
+ int render;
+ int anim_init;
+ int quick_step;
+ struct PTCacheID pid;
+
+ void (*update_progress)(void *data, float progress, int *cancel);
+ void *bake_job;
+} PTCacheBaker;
+
+/* PTCacheEditKey->flag */
+#define PEK_SELECT 1
+#define PEK_TAG 2
+#define PEK_HIDE 4
+#define PEK_USE_WCO 8
+
+typedef struct PTCacheEditKey {
+ float *co;
+ float *vel;
+ float *rot;
+ float *time;
+
+ float world_co[3];
+ float ftime;
+ float length;
+ short flag;
+} PTCacheEditKey;
+
+/* PTCacheEditPoint->flag */
+#define PEP_TAG 1
+#define PEP_EDIT_RECALC 2
+#define PEP_TRANSFORM 4
+#define PEP_HIDE 8
+
+typedef struct PTCacheEditPoint {
+ struct PTCacheEditKey *keys;
+ int totkey;
+ short flag;
+} PTCacheEditPoint;
+
+typedef struct PTCacheUndo {
+ struct PTCacheUndo *next, *prev;
+ struct PTCacheEditPoint *points;
+
+ /* particles stuff */
+ struct ParticleData *particles;
+ struct KDTree *emitter_field;
+ float *emitter_cosnos;
+ int psys_flag;
+
+ /* cache stuff */
+ struct ListBase mem_cache;
+
+ int totpoint;
+ char name[64];
+} PTCacheUndo;
+
+typedef struct PTCacheEdit {
+ ListBase undo;
+ struct PTCacheUndo *curundo;
+ PTCacheEditPoint *points;
+
+ struct PTCacheID pid;
+
+ /* particles stuff */
+ struct ParticleSystem *psys;
+ struct KDTree *emitter_field;
+ float *emitter_cosnos; /* localspace face centers and normals (average of its verts), from the derived mesh */
+ int *mirror_cache;
+
+ struct ParticleCacheKey **pathcache; /* path cache (runtime) */
+ ListBase pathcachebufs;
+
+ int totpoint, totframes, totcached, edited;
+
+ unsigned char sel_col[3];
+ unsigned char nosel_col[3];
+} PTCacheEdit;
+
+/* Particle functions */
+void BKE_ptcache_make_particle_key(struct ParticleKey *key, int index, void **data, float time);
+
+/**************** Creating ID's ****************************/
+void BKE_ptcache_id_from_softbody(PTCacheID *pid, struct Object *ob, struct SoftBody *sb);
+void BKE_ptcache_id_from_particles(PTCacheID *pid, struct Object *ob, struct ParticleSystem *psys);
+void BKE_ptcache_id_from_cloth(PTCacheID *pid, struct Object *ob, struct ClothModifierData *clmd);
+void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct SmokeModifierData *smd);
+void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, struct Object *ob, struct DynamicPaintSurface *surface);
+void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct RigidBodyWorld *rbw);
+
+void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob, struct Scene *scene, int duplis);
+
+/***************** Global funcs ****************************/
+void BKE_ptcache_remove(void);
+
+/************ ID specific functions ************************/
+void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra);
+int BKE_ptcache_id_exist(PTCacheID *id, int cfra);
+int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode);
+void BKE_ptcache_id_time(PTCacheID *pid, struct Scene *scene, float cfra, int *startframe, int *endframe, float *timescale);
+int BKE_ptcache_object_reset(struct Scene *scene, struct Object *ob, int mode);
+
+void BKE_ptcache_update_info(PTCacheID *pid);
+
+/*********** General cache reading/writing ******************/
+
+/* Size of cache data type. */
+int BKE_ptcache_data_size(int data_type);
+
+/* Is point with indes in memory cache */
+int BKE_ptcache_mem_index_find(struct PTCacheMem *pm, unsigned int index);
+
+/* Memory cache read/write helpers. */
+void BKE_ptcache_mem_pointers_init(struct PTCacheMem *pm);
+void BKE_ptcache_mem_pointers_incr(struct PTCacheMem *pm);
+int BKE_ptcache_mem_pointers_seek(int point_index, struct PTCacheMem *pm);
+
+/* Main cache reading call. */
+int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old);
+
+/* Main cache writing call. */
+int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra);
+
+/******************* Allocate & free ***************/
+struct PointCache *BKE_ptcache_add(struct ListBase *ptcaches);
+void BKE_ptcache_free_mem(struct ListBase *mem_cache);
+void BKE_ptcache_free(struct PointCache *cache);
+void BKE_ptcache_free_list(struct ListBase *ptcaches);
+struct PointCache *BKE_ptcache_copy_list(struct ListBase *ptcaches_new, const struct ListBase *ptcaches_old, bool copy_data);
+
+/********************** Baking *********************/
+
+/* Bakes cache with cache_step sized jumps in time, not accurate but very fast. */
+void BKE_ptcache_quick_cache_all(struct Main *bmain, struct Scene *scene);
+
+/* Bake cache or simulate to current frame with settings defined in the baker. */
+void BKE_ptcache_bake(struct PTCacheBaker *baker);
+
+/* Convert disk cache to memory cache. */
+void BKE_ptcache_disk_to_mem(struct PTCacheID *pid);
+
+/* Convert memory cache to disk cache. */
+void BKE_ptcache_mem_to_disk(struct PTCacheID *pid);
+
+/* Convert disk cache to memory cache and vice versa. Clears the cache that was converted. */
+void BKE_ptcache_toggle_disk_cache(struct PTCacheID *pid);
+
+/* Rename all disk cache files with a new name. Doesn't touch the actual content of the files. */
+void BKE_ptcache_disk_cache_rename(struct PTCacheID *pid, const char *name_src, const char *name_dst);
+
+/* Loads simulation from external (disk) cache files. */
+void BKE_ptcache_load_external(struct PTCacheID *pid);
+
+/* Set correct flags after successful simulation step */
+void BKE_ptcache_validate(struct PointCache *cache, int framenr);
+
+/* Set correct flags after unsuccessful simulation step */
+void BKE_ptcache_invalidate(struct PointCache *cache);
+
+#endif
diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h
index 20b16b6cc2d..965a97f08ba 100644
--- a/source/blender/blenkernel/BKE_rigidbody.h
+++ b/source/blender/blenkernel/BKE_rigidbody.h
@@ -98,6 +98,7 @@ void BKE_rigidbody_remove_constraint(struct Scene *scene, struct Object *ob);
void BKE_rigidbody_aftertrans_update(struct Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle);
void BKE_rigidbody_sync_transforms(struct RigidBodyWorld *rbw, struct Object *ob, float ctime);
bool BKE_rigidbody_check_sim_running(struct RigidBodyWorld *rbw, float ctime);
+void BKE_rigidbody_cache_reset(struct RigidBodyWorld *rbw);
void BKE_rigidbody_rebuild_world(struct Scene *scene, float ctime);
void BKE_rigidbody_do_simulation(struct Scene *scene, float ctime);
diff --git a/source/blender/blenkernel/BKE_sca.h b/source/blender/blenkernel/BKE_sca.h
index 2993540bb4f..a504f1bac3d 100644
--- a/source/blender/blenkernel/BKE_sca.h
+++ b/source/blender/blenkernel/BKE_sca.h
@@ -86,7 +86,7 @@ void BKE_sca_controllers_id_loop(struct ListBase *contlist, SCAControllerIDFunc
void BKE_sca_actuators_id_loop(struct ListBase *atclist, SCAActuatorIDFunc func, void *userdata);
-const char *sca_state_name_get(struct Object *ob, short bit);
+const char *sca_state_name_get(Object *ob, short bit);
#endif
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index 4bce0cd609c..486fe8ed5a8 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -68,7 +68,7 @@ extern void sbObjectToSoftbody(struct Object *ob);
/* pass NULL to unlink again */
extern void sbSetInterruptCallBack(int (*f)(void));
-extern void SB_estimate_transform(struct Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]);
+extern void SB_estimate_transform(Object *ob, float lloc[3], float lrot[3][3], float lscale[3][3]);
#endif
diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h
index df1fd945eb4..1c5ea946f59 100644
--- a/source/blender/blenkernel/BKE_texture.h
+++ b/source/blender/blenkernel/BKE_texture.h
@@ -47,6 +47,7 @@ struct Main;
struct Material;
struct MTex;
struct OceanTex;
+struct ParticleSettings;
struct PointDensity;
struct Tex;
struct TexMapping;
@@ -86,6 +87,7 @@ struct Tex *give_current_lamp_texture(struct Lamp *la);
struct Tex *give_current_linestyle_texture(struct FreestyleLineStyle *linestyle);
struct Tex *give_current_world_texture(struct World *world);
struct Tex *give_current_brush_texture(struct Brush *br);
+struct Tex *give_current_particle_texture(struct ParticleSettings *part);
struct bNode *give_current_material_texture_node(struct Material *ma);
@@ -97,6 +99,7 @@ void set_current_world_texture(struct World *wo, struct Tex *tex);
void set_current_material_texture(struct Material *ma, struct Tex *tex);
void set_current_lamp_texture(struct Lamp *la, struct Tex *tex);
void set_current_linestyle_texture(struct FreestyleLineStyle *linestyle, struct Tex *tex);
+void set_current_particle_texture(struct ParticleSettings *part, struct Tex *tex);
bool has_current_material_texture(struct Material *ma);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index ca55ba0226a..157c4408d6a 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -76,6 +76,7 @@ set(SRC
intern/blender_undo.c
intern/blendfile.c
intern/bmfont.c
+ intern/boids.c
intern/bpath.c
intern/brush.c
intern/bullet.c
@@ -147,8 +148,13 @@ set(SRC
intern/outliner_treehash.c
intern/packedFile.c
intern/paint.c
+ intern/particle.c
+ intern/particle_child.c
+ intern/particle_distribute.c
+ intern/particle_system.c
intern/pbvh.c
intern/pbvh_bmesh.c
+ intern/pointcache.c
intern/property.c
intern/report.c
intern/rigidbody.c
@@ -197,6 +203,7 @@ set(SRC
BKE_blendfile.h
BKE_bmfont.h
BKE_bmfont_types.h
+ BKE_boids.h
BKE_bpath.h
BKE_brush.h
BKE_bullet.h
@@ -261,7 +268,9 @@ set(SRC
BKE_outliner_treehash.h
BKE_packedFile.h
BKE_paint.h
+ BKE_particle.h
BKE_pbvh.h
+ BKE_pointcache.h
BKE_property.h
BKE_report.h
BKE_rigidbody.h
diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c
index 17d76bb290a..7d3d12ac112 100644
--- a/source/blender/blenkernel/intern/anim.c
+++ b/source/blender/blenkernel/intern/anim.c
@@ -41,7 +41,6 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_key_types.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_curve.h"
@@ -50,6 +49,7 @@
#include "BKE_key.h"
#include "BKE_main.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_anim.h"
#include "BKE_report.h"
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 9bf4eba0f7a..21279392392 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -88,6 +88,7 @@ bool id_type_can_have_animdata(const short id_type)
case ID_OB:
case ID_ME: case ID_MB: case ID_CU: case ID_AR: case ID_LT:
case ID_KE:
+ case ID_PA:
case ID_MA: case ID_TE: case ID_NT:
case ID_LA: case ID_CA: case ID_WO:
case ID_LS:
@@ -1136,6 +1137,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u
/* meshes */
ANIMDATA_IDS_CB(mainptr->mesh.first);
+ /* particles */
+ ANIMDATA_IDS_CB(mainptr->particle.first);
+
/* speakers */
ANIMDATA_IDS_CB(mainptr->speaker.first);
@@ -1229,6 +1233,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const cha
/* meshes */
RENAMEFIX_ANIM_IDS(mainptr->mesh.first);
+ /* particles */
+ RENAMEFIX_ANIM_IDS(mainptr->particle.first);
+
/* speakers */
RENAMEFIX_ANIM_IDS(mainptr->speaker.first);
@@ -2861,6 +2868,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime)
/* meshes */
EVAL_ANIM_IDS(main->mesh.first, ADT_RECALC_ANIM);
+ /* particles */
+ EVAL_ANIM_IDS(main->particle.first, ADT_RECALC_ANIM);
+
/* speakers */
EVAL_ANIM_IDS(main->speaker.first, ADT_RECALC_ANIM);
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
new file mode 100644
index 00000000000..b4bc83bf94c
--- /dev/null
+++ b/source/blender/blenkernel/intern/boids.c
@@ -0,0 +1,1618 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/boids.c
+ * \ingroup bke
+ */
+
+
+#include <string.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_force.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_rand.h"
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_kdtree.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_collision.h"
+#include "BKE_effect.h"
+#include "BKE_boids.h"
+#include "BKE_particle.h"
+
+#include "BKE_modifier.h"
+
+#include "RNA_enum_types.h"
+
+typedef struct BoidValues {
+ float max_speed, max_acc;
+ float max_ave, min_speed;
+ float personal_space, jump_speed;
+} BoidValues;
+
+static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness);
+
+static int rule_none(BoidRule *UNUSED(rule), BoidBrainData *UNUSED(data), BoidValues *UNUSED(val), ParticleData *UNUSED(pa))
+{
+ return 0;
+}
+
+static int rule_goal_avoid(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+ BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid*) rule;
+ BoidSettings *boids = bbd->part->boids;
+ BoidParticle *bpa = pa->boid;
+ EffectedPoint epoint;
+ ListBase *effectors = bbd->sim->psys->effectors;
+ EffectorCache *cur, *eff = NULL;
+ EffectorCache temp_eff;
+ EffectorData efd, cur_efd;
+ float mul = (rule->type == eBoidRuleType_Avoid ? 1.0 : -1.0);
+ float priority = 0.0f, len = 0.0f;
+ int ret = 0;
+
+ int p = 0;
+ efd.index = cur_efd.index = &p;
+
+ pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint);
+
+ /* first find out goal/predator with highest priority */
+ if (effectors) for (cur = effectors->first; cur; cur=cur->next) {
+ Object *eob = cur->ob;
+ PartDeflect *pd = cur->pd;
+
+ if (gabr->ob && (rule->type != eBoidRuleType_Goal || gabr->ob != bpa->ground)) {
+ if (gabr->ob == eob) {
+ /* TODO: effectors with multiple points */
+ if (get_effector_data(cur, &efd, &epoint, 0)) {
+ if (cur->pd && cur->pd->forcefield == PFIELD_BOID)
+ priority = mul * pd->f_strength * effector_falloff(cur, &efd, &epoint, bbd->part->effector_weights);
+ else
+ priority = 1.0;
+
+ eff = cur;
+ }
+ break;
+ }
+ }
+ else if (rule->type == eBoidRuleType_Goal && eob == bpa->ground) {
+ /* skip current object */
+ }
+ else if (pd->forcefield == PFIELD_BOID && mul * pd->f_strength > 0.0f && get_effector_data(cur, &cur_efd, &epoint, 0)) {
+ float temp = mul * pd->f_strength * effector_falloff(cur, &cur_efd, &epoint, bbd->part->effector_weights);
+
+ if (temp == 0.0f) {
+ /* do nothing */
+ }
+ else if (temp > priority) {
+ priority = temp;
+ eff = cur;
+ efd = cur_efd;
+ len = efd.distance;
+ }
+ /* choose closest object with same priority */
+ else if (temp == priority && efd.distance < len) {
+ eff = cur;
+ efd = cur_efd;
+ len = efd.distance;
+ }
+ }
+ }
+
+ /* if the object doesn't have effector data we have to fake it */
+ if (eff == NULL && gabr->ob) {
+ memset(&temp_eff, 0, sizeof(EffectorCache));
+ temp_eff.ob = gabr->ob;
+ temp_eff.scene = bbd->sim->scene;
+ eff = &temp_eff;
+ get_effector_data(eff, &efd, &epoint, 0);
+ priority = 1.0f;
+ }
+
+ /* then use that effector */
+ if (priority > (rule->type==eBoidRuleType_Avoid ? gabr->fear_factor : 0.0f)) { /* with avoid, factor is "fear factor" */
+ Object *eob = eff->ob;
+ PartDeflect *pd = eff->pd;
+ float surface = (pd && pd->shape == PFIELD_SHAPE_SURFACE) ? 1.0f : 0.0f;
+
+ if (gabr->options & BRULE_GOAL_AVOID_PREDICT) {
+ /* estimate future location of target */
+ get_effector_data(eff, &efd, &epoint, 1);
+
+ mul_v3_fl(efd.vel, efd.distance / (val->max_speed * bbd->timestep));
+ add_v3_v3(efd.loc, efd.vel);
+ sub_v3_v3v3(efd.vec_to_point, pa->prev_state.co, efd.loc);
+ efd.distance = len_v3(efd.vec_to_point);
+ }
+
+ if (rule->type == eBoidRuleType_Goal && boids->options & BOID_ALLOW_CLIMB && surface!=0.0f) {
+ if (!bbd->goal_ob || bbd->goal_priority < priority) {
+ bbd->goal_ob = eob;
+ copy_v3_v3(bbd->goal_co, efd.loc);
+ copy_v3_v3(bbd->goal_nor, efd.nor);
+ }
+ }
+ else if (rule->type == eBoidRuleType_Avoid && bpa->data.mode == eBoidMode_Climbing &&
+ priority > 2.0f * gabr->fear_factor) {
+ /* detach from surface and try to fly away from danger */
+ negate_v3_v3(efd.vec_to_point, bpa->gravity);
+ }
+
+ copy_v3_v3(bbd->wanted_co, efd.vec_to_point);
+ mul_v3_fl(bbd->wanted_co, mul);
+
+ bbd->wanted_speed = val->max_speed * priority;
+
+ /* with goals factor is approach velocity factor */
+ if (rule->type == eBoidRuleType_Goal && boids->landing_smoothness > 0.0f) {
+ float len2 = 2.0f*len_v3(pa->prev_state.vel);
+
+ surface *= pa->size * boids->height;
+
+ if (len2 > 0.0f && efd.distance - surface < len2) {
+ len2 = (efd.distance - surface)/len2;
+ bbd->wanted_speed *= powf(len2, boids->landing_smoothness);
+ }
+ }
+
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int rule_avoid_collision(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+ const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+ BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision*) rule;
+ KDTreeNearest *ptn = NULL;
+ ParticleTarget *pt;
+ BoidParticle *bpa = pa->boid;
+ ColliderCache *coll;
+ float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
+ float co1[3], vel1[3], co2[3], vel2[3];
+ float len, t, inp, t_min = 2.0f;
+ int n, neighbors = 0, nearest = 0;
+ int ret = 0;
+
+ //check deflector objects first
+ if (acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) {
+ ParticleCollision col;
+ BVHTreeRayHit hit;
+ float radius = val->personal_space * pa->size, ray_dir[3];
+
+ memset(&col, 0, sizeof(ParticleCollision));
+
+ copy_v3_v3(col.co1, pa->prev_state.co);
+ add_v3_v3v3(col.co2, pa->prev_state.co, pa->prev_state.vel);
+ sub_v3_v3v3(ray_dir, col.co2, col.co1);
+ mul_v3_fl(ray_dir, acbr->look_ahead);
+ col.f = 0.0f;
+ hit.index = -1;
+ hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+
+ /* find out closest deflector object */
+ for (coll = bbd->sim->colliders->first; coll; coll=coll->next) {
+ /* don't check with current ground object */
+ if (coll->ob == bpa->ground)
+ continue;
+
+ col.current = coll->ob;
+ col.md = coll->collmd;
+
+ if (col.md && col.md->bvhtree) {
+ BLI_bvhtree_ray_cast_ex(
+ col.md->bvhtree, col.co1, ray_dir, radius, &hit,
+ BKE_psys_collision_neartest_cb, &col, raycast_flag);
+ }
+ }
+ /* then avoid that object */
+ if (hit.index>=0) {
+ t = hit.dist/col.original_ray_length;
+
+ /* avoid head-on collision */
+ if (dot_v3v3(col.pce.nor, pa->prev_state.ave) < -0.99f) {
+ /* don't know why, but uneven range [0.0, 1.0] */
+ /* works much better than even [-1.0, 1.0] */
+ bbd->wanted_co[0] = BLI_rng_get_float(bbd->rng);
+ bbd->wanted_co[1] = BLI_rng_get_float(bbd->rng);
+ bbd->wanted_co[2] = BLI_rng_get_float(bbd->rng);
+ }
+ else {
+ copy_v3_v3(bbd->wanted_co, col.pce.nor);
+ }
+
+ mul_v3_fl(bbd->wanted_co, (1.0f - t) * val->personal_space * pa->size);
+
+ bbd->wanted_speed = sqrtf(t) * len_v3(pa->prev_state.vel);
+ bbd->wanted_speed = MAX2(bbd->wanted_speed, val->min_speed);
+
+ return 1;
+ }
+ }
+
+ //check boids in own system
+ if (acbr->options & BRULE_ACOLL_WITH_BOIDS) {
+ neighbors = BLI_kdtree_range_search__normal(
+ bbd->sim->psys->tree, pa->prev_state.co, pa->prev_state.ave,
+ &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel));
+ if (neighbors > 1) for (n=1; n<neighbors; n++) {
+ copy_v3_v3(co1, pa->prev_state.co);
+ copy_v3_v3(vel1, pa->prev_state.vel);
+ copy_v3_v3(co2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.co);
+ copy_v3_v3(vel2, (bbd->sim->psys->particles + ptn[n].index)->prev_state.vel);
+
+ sub_v3_v3v3(loc, co1, co2);
+
+ sub_v3_v3v3(vec, vel1, vel2);
+
+ inp = dot_v3v3(vec, vec);
+
+ /* velocities not parallel */
+ if (inp != 0.0f) {
+ t = -dot_v3v3(loc, vec)/inp;
+ /* cpa is not too far in the future so investigate further */
+ if (t > 0.0f && t < t_min) {
+ madd_v3_v3fl(co1, vel1, t);
+ madd_v3_v3fl(co2, vel2, t);
+
+ sub_v3_v3v3(vec, co2, co1);
+
+ len = normalize_v3(vec);
+
+ /* distance of cpa is close enough */
+ if (len < 2.0f * val->personal_space * pa->size) {
+ t_min = t;
+
+ mul_v3_fl(vec, len_v3(vel1));
+ mul_v3_fl(vec, (2.0f - t)/2.0f);
+ sub_v3_v3v3(bbd->wanted_co, vel1, vec);
+ bbd->wanted_speed = len_v3(bbd->wanted_co);
+ ret = 1;
+ }
+ }
+ }
+ }
+ }
+ if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+
+ /* check boids in other systems */
+ for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) {
+ ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
+
+ if (epsys) {
+ BLI_assert(epsys->tree != NULL);
+ neighbors = BLI_kdtree_range_search__normal(
+ epsys->tree, pa->prev_state.co, pa->prev_state.ave,
+ &ptn, acbr->look_ahead * len_v3(pa->prev_state.vel));
+
+ if (neighbors > 0) for (n=0; n<neighbors; n++) {
+ copy_v3_v3(co1, pa->prev_state.co);
+ copy_v3_v3(vel1, pa->prev_state.vel);
+ copy_v3_v3(co2, (epsys->particles + ptn[n].index)->prev_state.co);
+ copy_v3_v3(vel2, (epsys->particles + ptn[n].index)->prev_state.vel);
+
+ sub_v3_v3v3(loc, co1, co2);
+
+ sub_v3_v3v3(vec, vel1, vel2);
+
+ inp = dot_v3v3(vec, vec);
+
+ /* velocities not parallel */
+ if (inp != 0.0f) {
+ t = -dot_v3v3(loc, vec)/inp;
+ /* cpa is not too far in the future so investigate further */
+ if (t > 0.0f && t < t_min) {
+ madd_v3_v3fl(co1, vel1, t);
+ madd_v3_v3fl(co2, vel2, t);
+
+ sub_v3_v3v3(vec, co2, co1);
+
+ len = normalize_v3(vec);
+
+ /* distance of cpa is close enough */
+ if (len < 2.0f * val->personal_space * pa->size) {
+ t_min = t;
+
+ mul_v3_fl(vec, len_v3(vel1));
+ mul_v3_fl(vec, (2.0f - t)/2.0f);
+ sub_v3_v3v3(bbd->wanted_co, vel1, vec);
+ bbd->wanted_speed = len_v3(bbd->wanted_co);
+ ret = 1;
+ }
+ }
+ }
+ }
+
+ if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+ }
+ }
+
+
+ if (ptn && nearest==0)
+ MEM_freeN(ptn);
+
+ return ret;
+}
+static int rule_separate(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+ KDTreeNearest *ptn = NULL;
+ ParticleTarget *pt;
+ float len = 2.0f * val->personal_space * pa->size + 1.0f;
+ float vec[3] = {0.0f, 0.0f, 0.0f};
+ int neighbors = BLI_kdtree_range_search(
+ bbd->sim->psys->tree, pa->prev_state.co,
+ &ptn, 2.0f * val->personal_space * pa->size);
+ int ret = 0;
+
+ if (neighbors > 1 && ptn[1].dist!=0.0f) {
+ sub_v3_v3v3(vec, pa->prev_state.co, bbd->sim->psys->particles[ptn[1].index].state.co);
+ mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[1].dist) / ptn[1].dist);
+ add_v3_v3(bbd->wanted_co, vec);
+ bbd->wanted_speed = val->max_speed;
+ len = ptn[1].dist;
+ ret = 1;
+ }
+ if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+
+ /* check other boid systems */
+ for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) {
+ ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
+
+ if (epsys) {
+ neighbors = BLI_kdtree_range_search(
+ epsys->tree, pa->prev_state.co,
+ &ptn, 2.0f * val->personal_space * pa->size);
+
+ if (neighbors > 0 && ptn[0].dist < len) {
+ sub_v3_v3v3(vec, pa->prev_state.co, ptn[0].co);
+ mul_v3_fl(vec, (2.0f * val->personal_space * pa->size - ptn[0].dist) / ptn[1].dist);
+ add_v3_v3(bbd->wanted_co, vec);
+ bbd->wanted_speed = val->max_speed;
+ len = ptn[0].dist;
+ ret = 1;
+ }
+
+ if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+ }
+ }
+ return ret;
+}
+static int rule_flock(BoidRule *UNUSED(rule), BoidBrainData *bbd, BoidValues *UNUSED(val), ParticleData *pa)
+{
+ KDTreeNearest ptn[11];
+ float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
+ int neighbors = BLI_kdtree_find_nearest_n__normal(bbd->sim->psys->tree, pa->state.co, pa->prev_state.ave, ptn, 11);
+ int n;
+ int ret = 0;
+
+ if (neighbors > 1) {
+ for (n=1; n<neighbors; n++) {
+ add_v3_v3(loc, bbd->sim->psys->particles[ptn[n].index].prev_state.co);
+ add_v3_v3(vec, bbd->sim->psys->particles[ptn[n].index].prev_state.vel);
+ }
+
+ mul_v3_fl(loc, 1.0f/((float)neighbors - 1.0f));
+ mul_v3_fl(vec, 1.0f/((float)neighbors - 1.0f));
+
+ sub_v3_v3(loc, pa->prev_state.co);
+ sub_v3_v3(vec, pa->prev_state.vel);
+
+ add_v3_v3(bbd->wanted_co, vec);
+ add_v3_v3(bbd->wanted_co, loc);
+ bbd->wanted_speed = len_v3(bbd->wanted_co);
+
+ ret = 1;
+ }
+ return ret;
+}
+static int rule_follow_leader(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+ BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule;
+ float vec[3] = {0.0f, 0.0f, 0.0f}, loc[3] = {0.0f, 0.0f, 0.0f};
+ float mul, len;
+ int n = (flbr->queue_size <= 1) ? bbd->sim->psys->totpart : flbr->queue_size;
+ int i, ret = 0, p = pa - bbd->sim->psys->particles;
+
+ if (flbr->ob) {
+ float vec2[3], t;
+
+ /* first check we're not blocking the leader */
+ sub_v3_v3v3(vec, flbr->loc, flbr->oloc);
+ mul_v3_fl(vec, 1.0f/bbd->timestep);
+
+ sub_v3_v3v3(loc, pa->prev_state.co, flbr->oloc);
+
+ mul = dot_v3v3(vec, vec);
+
+ /* leader is not moving */
+ if (mul < 0.01f) {
+ len = len_v3(loc);
+ /* too close to leader */
+ if (len < 2.0f * val->personal_space * pa->size) {
+ copy_v3_v3(bbd->wanted_co, loc);
+ bbd->wanted_speed = val->max_speed;
+ return 1;
+ }
+ }
+ else {
+ t = dot_v3v3(loc, vec)/mul;
+
+ /* possible blocking of leader in near future */
+ if (t > 0.0f && t < 3.0f) {
+ copy_v3_v3(vec2, vec);
+ mul_v3_fl(vec2, t);
+
+ sub_v3_v3v3(vec2, loc, vec2);
+
+ len = len_v3(vec2);
+
+ if (len < 2.0f * val->personal_space * pa->size) {
+ copy_v3_v3(bbd->wanted_co, vec2);
+ bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f;
+ return 1;
+ }
+ }
+ }
+
+ /* not blocking so try to follow leader */
+ if (p && flbr->options & BRULE_LEADER_IN_LINE) {
+ copy_v3_v3(vec, bbd->sim->psys->particles[p-1].prev_state.vel);
+ copy_v3_v3(loc, bbd->sim->psys->particles[p-1].prev_state.co);
+ }
+ else {
+ copy_v3_v3(loc, flbr->oloc);
+ sub_v3_v3v3(vec, flbr->loc, flbr->oloc);
+ mul_v3_fl(vec, 1.0f/bbd->timestep);
+ }
+
+ /* fac is seconds behind leader */
+ madd_v3_v3fl(loc, vec, -flbr->distance);
+
+ sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co);
+ bbd->wanted_speed = len_v3(bbd->wanted_co);
+
+ ret = 1;
+ }
+ else if (p % n) {
+ float vec2[3], t, t_min = 3.0f;
+
+ /* first check we're not blocking any leaders */
+ for (i = 0; i< bbd->sim->psys->totpart; i+=n) {
+ copy_v3_v3(vec, bbd->sim->psys->particles[i].prev_state.vel);
+
+ sub_v3_v3v3(loc, pa->prev_state.co, bbd->sim->psys->particles[i].prev_state.co);
+
+ mul = dot_v3v3(vec, vec);
+
+ /* leader is not moving */
+ if (mul < 0.01f) {
+ len = len_v3(loc);
+ /* too close to leader */
+ if (len < 2.0f * val->personal_space * pa->size) {
+ copy_v3_v3(bbd->wanted_co, loc);
+ bbd->wanted_speed = val->max_speed;
+ return 1;
+ }
+ }
+ else {
+ t = dot_v3v3(loc, vec)/mul;
+
+ /* possible blocking of leader in near future */
+ if (t > 0.0f && t < t_min) {
+ copy_v3_v3(vec2, vec);
+ mul_v3_fl(vec2, t);
+
+ sub_v3_v3v3(vec2, loc, vec2);
+
+ len = len_v3(vec2);
+
+ if (len < 2.0f * val->personal_space * pa->size) {
+ t_min = t;
+ copy_v3_v3(bbd->wanted_co, loc);
+ bbd->wanted_speed = val->max_speed * (3.0f - t)/3.0f;
+ ret = 1;
+ }
+ }
+ }
+ }
+
+ if (ret) return 1;
+
+ /* not blocking so try to follow leader */
+ if (flbr->options & BRULE_LEADER_IN_LINE) {
+ copy_v3_v3(vec, bbd->sim->psys->particles[p-1].prev_state.vel);
+ copy_v3_v3(loc, bbd->sim->psys->particles[p-1].prev_state.co);
+ }
+ else {
+ copy_v3_v3(vec, bbd->sim->psys->particles[p - p%n].prev_state.vel);
+ copy_v3_v3(loc, bbd->sim->psys->particles[p - p%n].prev_state.co);
+ }
+
+ /* fac is seconds behind leader */
+ madd_v3_v3fl(loc, vec, -flbr->distance);
+
+ sub_v3_v3v3(bbd->wanted_co, loc, pa->prev_state.co);
+ bbd->wanted_speed = len_v3(bbd->wanted_co);
+
+ ret = 1;
+ }
+
+ return ret;
+}
+static int rule_average_speed(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+ BoidParticle *bpa = pa->boid;
+ BoidRuleAverageSpeed *asbr = (BoidRuleAverageSpeed*)rule;
+ float vec[3] = {0.0f, 0.0f, 0.0f};
+
+ if (asbr->wander > 0.0f) {
+ /* abuse pa->r_ave for wandering */
+ bpa->wander[0] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
+ bpa->wander[1] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
+ bpa->wander[2] += asbr->wander * (-1.0f + 2.0f * BLI_rng_get_float(bbd->rng));
+
+ normalize_v3(bpa->wander);
+
+ copy_v3_v3(vec, bpa->wander);
+
+ mul_qt_v3(pa->prev_state.rot, vec);
+
+ copy_v3_v3(bbd->wanted_co, pa->prev_state.ave);
+
+ mul_v3_fl(bbd->wanted_co, 1.1f);
+
+ add_v3_v3(bbd->wanted_co, vec);
+
+ /* leveling */
+ if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) {
+ project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity);
+ mul_v3_fl(vec, asbr->level);
+ sub_v3_v3(bbd->wanted_co, vec);
+ }
+ }
+ else {
+ copy_v3_v3(bbd->wanted_co, pa->prev_state.ave);
+
+ /* may happen at birth */
+ if (dot_v2v2(bbd->wanted_co, bbd->wanted_co)==0.0f) {
+ bbd->wanted_co[0] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+ bbd->wanted_co[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+ bbd->wanted_co[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+ }
+
+ /* leveling */
+ if (asbr->level > 0.0f && psys_uses_gravity(bbd->sim)) {
+ project_v3_v3v3(vec, bbd->wanted_co, bbd->sim->scene->physics_settings.gravity);
+ mul_v3_fl(vec, asbr->level);
+ sub_v3_v3(bbd->wanted_co, vec);
+ }
+
+ }
+ bbd->wanted_speed = asbr->speed * val->max_speed;
+
+ return 1;
+}
+static int rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, ParticleData *pa)
+{
+ BoidRuleFight *fbr = (BoidRuleFight*)rule;
+ KDTreeNearest *ptn = NULL;
+ ParticleTarget *pt;
+ ParticleData *epars;
+ ParticleData *enemy_pa = NULL;
+ BoidParticle *bpa;
+ /* friends & enemies */
+ float closest_enemy[3] = {0.0f, 0.0f, 0.0f};
+ float closest_dist = fbr->distance + 1.0f;
+ float f_strength = 0.0f, e_strength = 0.0f;
+ float health = 0.0f;
+ int n, ret = 0;
+
+ /* calculate own group strength */
+ int neighbors = BLI_kdtree_range_search(
+ bbd->sim->psys->tree, pa->prev_state.co,
+ &ptn, fbr->distance);
+ for (n=0; n<neighbors; n++) {
+ bpa = bbd->sim->psys->particles[ptn[n].index].boid;
+ health += bpa->data.health;
+ }
+
+ f_strength += bbd->part->boids->strength * health;
+
+ if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+
+ /* add other friendlies and calculate enemy strength and find closest enemy */
+ for (pt=bbd->sim->psys->targets.first; pt; pt=pt->next) {
+ ParticleSystem *epsys = psys_get_target_system(bbd->sim->ob, pt);
+ if (epsys) {
+ epars = epsys->particles;
+
+ neighbors = BLI_kdtree_range_search(
+ epsys->tree, pa->prev_state.co,
+ &ptn, fbr->distance);
+
+ health = 0.0f;
+
+ for (n=0; n<neighbors; n++) {
+ bpa = epars[ptn[n].index].boid;
+ health += bpa->data.health;
+
+ if (n==0 && pt->mode==PTARGET_MODE_ENEMY && ptn[n].dist < closest_dist) {
+ copy_v3_v3(closest_enemy, ptn[n].co);
+ closest_dist = ptn[n].dist;
+ enemy_pa = epars + ptn[n].index;
+ }
+ }
+ if (pt->mode==PTARGET_MODE_ENEMY)
+ e_strength += epsys->part->boids->strength * health;
+ else if (pt->mode==PTARGET_MODE_FRIEND)
+ f_strength += epsys->part->boids->strength * health;
+
+ if (ptn) { MEM_freeN(ptn); ptn=NULL; }
+ }
+ }
+ /* decide action if enemy presence found */
+ if (e_strength > 0.0f) {
+ sub_v3_v3v3(bbd->wanted_co, closest_enemy, pa->prev_state.co);
+
+ /* attack if in range */
+ if (closest_dist <= bbd->part->boids->range + pa->size + enemy_pa->size) {
+ float damage = BLI_rng_get_float(bbd->rng);
+ float enemy_dir[3];
+
+ normalize_v3_v3(enemy_dir, bbd->wanted_co);
+
+ /* fight mode */
+ bbd->wanted_speed = 0.0f;
+
+ /* must face enemy to fight */
+ if (dot_v3v3(pa->prev_state.ave, enemy_dir)>0.5f) {
+ bpa = enemy_pa->boid;
+ bpa->data.health -= bbd->part->boids->strength * bbd->timestep * ((1.0f-bbd->part->boids->accuracy)*damage + bbd->part->boids->accuracy);
+ }
+ }
+ else {
+ /* approach mode */
+ bbd->wanted_speed = val->max_speed;
+ }
+
+ /* check if boid doesn't want to fight */
+ bpa = pa->boid;
+ if (bpa->data.health/bbd->part->boids->health * bbd->part->boids->aggression < e_strength / f_strength) {
+ /* decide to flee */
+ if (closest_dist < fbr->flee_distance * fbr->distance) {
+ negate_v3(bbd->wanted_co);
+ bbd->wanted_speed = val->max_speed;
+ }
+ else { /* wait for better odds */
+ bbd->wanted_speed = 0.0f;
+ }
+ }
+
+ ret = 1;
+ }
+
+ return ret;
+}
+
+typedef int (*boid_rule_cb)(BoidRule *rule, BoidBrainData *data, BoidValues *val, ParticleData *pa);
+
+static boid_rule_cb boid_rules[] = {
+ rule_none,
+ rule_goal_avoid,
+ rule_goal_avoid,
+ rule_avoid_collision,
+ rule_separate,
+ rule_flock,
+ rule_follow_leader,
+ rule_average_speed,
+ rule_fight,
+ //rule_help,
+ //rule_protect,
+ //rule_hide,
+ //rule_follow_path,
+ //rule_follow_wall
+};
+
+static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa)
+{
+ BoidParticle *bpa = pa->boid;
+
+ if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) {
+ val->max_speed = boids->land_max_speed * bpa->data.health/boids->health;
+ val->max_acc = boids->land_max_acc * val->max_speed;
+ val->max_ave = boids->land_max_ave * (float)M_PI * bpa->data.health/boids->health;
+ val->min_speed = 0.0f; /* no minimum speed on land */
+ val->personal_space = boids->land_personal_space;
+ val->jump_speed = boids->land_jump_speed * bpa->data.health/boids->health;
+ }
+ else {
+ val->max_speed = boids->air_max_speed * bpa->data.health/boids->health;
+ val->max_acc = boids->air_max_acc * val->max_speed;
+ val->max_ave = boids->air_max_ave * (float)M_PI * bpa->data.health/boids->health;
+ val->min_speed = boids->air_min_speed * boids->air_max_speed;
+ val->personal_space = boids->air_personal_space;
+ val->jump_speed = 0.0f; /* no jumping in air */
+ }
+}
+
+static Object *boid_find_ground(BoidBrainData *bbd, ParticleData *pa, float ground_co[3], float ground_nor[3])
+{
+ const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+ BoidParticle *bpa = pa->boid;
+
+ if (bpa->data.mode == eBoidMode_Climbing) {
+ SurfaceModifierData *surmd = NULL;
+ float x[3], v[3];
+
+ surmd = (SurfaceModifierData *)modifiers_findByType(bpa->ground, eModifierType_Surface );
+
+ /* take surface velocity into account */
+ closest_point_on_surface(surmd, pa->state.co, x, NULL, v);
+ add_v3_v3(x, v);
+
+ /* get actual position on surface */
+ closest_point_on_surface(surmd, x, ground_co, ground_nor, NULL);
+
+ return bpa->ground;
+ }
+ else {
+ float zvec[3] = {0.0f, 0.0f, 2000.0f};
+ ParticleCollision col;
+ ColliderCache *coll;
+ BVHTreeRayHit hit;
+ float radius = 0.0f, t, ray_dir[3];
+
+ if (!bbd->sim->colliders)
+ return NULL;
+
+ memset(&col, 0, sizeof(ParticleCollision));
+
+ /* first try to find below boid */
+ copy_v3_v3(col.co1, pa->state.co);
+ sub_v3_v3v3(col.co2, pa->state.co, zvec);
+ sub_v3_v3v3(ray_dir, col.co2, col.co1);
+ col.f = 0.0f;
+ hit.index = -1;
+ hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+ col.pce.inside = 0;
+
+ for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
+ col.current = coll->ob;
+ col.md = coll->collmd;
+ col.fac1 = col.fac2 = 0.f;
+
+ if (col.md && col.md->bvhtree) {
+ BLI_bvhtree_ray_cast_ex(
+ col.md->bvhtree, col.co1, ray_dir, radius, &hit,
+ BKE_psys_collision_neartest_cb, &col, raycast_flag);
+ }
+ }
+ /* then use that object */
+ if (hit.index>=0) {
+ t = hit.dist/col.original_ray_length;
+ interp_v3_v3v3(ground_co, col.co1, col.co2, t);
+ normalize_v3_v3(ground_nor, col.pce.nor);
+ return col.hit;
+ }
+
+ /* couldn't find below, so find upmost deflector object */
+ add_v3_v3v3(col.co1, pa->state.co, zvec);
+ sub_v3_v3v3(col.co2, pa->state.co, zvec);
+ sub_v3_v3(col.co2, zvec);
+ sub_v3_v3v3(ray_dir, col.co2, col.co1);
+ col.f = 0.0f;
+ hit.index = -1;
+ hit.dist = col.original_ray_length = normalize_v3(ray_dir);
+
+ for (coll = bbd->sim->colliders->first; coll; coll = coll->next) {
+ col.current = coll->ob;
+ col.md = coll->collmd;
+
+ if (col.md && col.md->bvhtree) {
+ BLI_bvhtree_ray_cast_ex(
+ col.md->bvhtree, col.co1, ray_dir, radius, &hit,
+ BKE_psys_collision_neartest_cb, &col, raycast_flag);
+ }
+ }
+ /* then use that object */
+ if (hit.index>=0) {
+ t = hit.dist/col.original_ray_length;
+ interp_v3_v3v3(ground_co, col.co1, col.co2, t);
+ normalize_v3_v3(ground_nor, col.pce.nor);
+ return col.hit;
+ }
+
+ /* default to z=0 */
+ copy_v3_v3(ground_co, pa->state.co);
+ ground_co[2] = 0;
+ ground_nor[0] = ground_nor[1] = 0.0f;
+ ground_nor[2] = 1.0f;
+ return NULL;
+ }
+}
+static int boid_rule_applies(ParticleData *pa, BoidSettings *UNUSED(boids), BoidRule *rule)
+{
+ BoidParticle *bpa = pa->boid;
+
+ if (rule==NULL)
+ return 0;
+
+ if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing) && rule->flag & BOIDRULE_ON_LAND)
+ return 1;
+
+ if (bpa->data.mode==eBoidMode_InAir && rule->flag & BOIDRULE_IN_AIR)
+ return 1;
+
+ return 0;
+}
+void boids_precalc_rules(ParticleSettings *part, float cfra)
+{
+ BoidState *state = part->boids->states.first;
+ BoidRule *rule;
+ for (; state; state=state->next) {
+ for (rule = state->rules.first; rule; rule=rule->next) {
+ if (rule->type==eBoidRuleType_FollowLeader) {
+ BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader*) rule;
+
+ if (flbr->ob && flbr->cfra != cfra) {
+ /* save object locations for velocity calculations */
+ copy_v3_v3(flbr->oloc, flbr->loc);
+ copy_v3_v3(flbr->loc, flbr->ob->obmat[3]);
+ flbr->cfra = cfra;
+ }
+ }
+ }
+ }
+}
+static void boid_climb(BoidSettings *boids, ParticleData *pa, float *surface_co, float *surface_nor)
+{
+ BoidParticle *bpa = pa->boid;
+ float nor[3], vel[3];
+ copy_v3_v3(nor, surface_nor);
+
+ /* gather apparent gravity */
+ madd_v3_v3fl(bpa->gravity, surface_nor, -1.0f);
+ normalize_v3(bpa->gravity);
+
+ /* raise boid it's size from surface */
+ mul_v3_fl(nor, pa->size * boids->height);
+ add_v3_v3v3(pa->state.co, surface_co, nor);
+
+ /* remove normal component from velocity */
+ project_v3_v3v3(vel, pa->state.vel, surface_nor);
+ sub_v3_v3v3(pa->state.vel, pa->state.vel, vel);
+}
+static float boid_goal_signed_dist(float *boid_co, float *goal_co, float *goal_nor)
+{
+ float vec[3];
+
+ sub_v3_v3v3(vec, boid_co, goal_co);
+
+ return dot_v3v3(vec, goal_nor);
+}
+/* wanted_co is relative to boid location */
+static int apply_boid_rule(BoidBrainData *bbd, BoidRule *rule, BoidValues *val, ParticleData *pa, float fuzziness)
+{
+ if (rule==NULL)
+ return 0;
+
+ if (boid_rule_applies(pa, bbd->part->boids, rule)==0)
+ return 0;
+
+ if (boid_rules[rule->type](rule, bbd, val, pa)==0)
+ return 0;
+
+ if (fuzziness < 0.0f || compare_len_v3v3(bbd->wanted_co, pa->prev_state.vel, fuzziness * len_v3(pa->prev_state.vel))==0)
+ return 1;
+ else
+ return 0;
+}
+static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa)
+{
+ BoidState *state = boids->states.first;
+ BoidParticle *bpa = pa->boid;
+
+ for (; state; state=state->next) {
+ if (state->id==bpa->data.state_id)
+ return state;
+ }
+
+ /* for some reason particle isn't at a valid state */
+ state = boids->states.first;
+ if (state)
+ bpa->data.state_id = state->id;
+
+ return state;
+}
+//static int boid_condition_is_true(BoidCondition *cond)
+//{
+// /* TODO */
+// return 0;
+//}
+
+/* determines the velocity the boid wants to have */
+void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
+{
+ BoidRule *rule;
+ BoidSettings *boids = bbd->part->boids;
+ BoidValues val;
+ BoidState *state = get_boid_state(boids, pa);
+ BoidParticle *bpa = pa->boid;
+ ParticleSystem *psys = bbd->sim->psys;
+ int rand;
+ //BoidCondition *cond;
+
+ if (bpa->data.health <= 0.0f) {
+ pa->alive = PARS_DYING;
+ pa->dietime = bbd->cfra;
+ return;
+ }
+
+ //planned for near future
+ //cond = state->conditions.first;
+ //for (; cond; cond=cond->next) {
+ // if (boid_condition_is_true(cond)) {
+ // pa->boid->state_id = cond->state_id;
+ // state = get_boid_state(boids, pa);
+ // break; /* only first true condition is used */
+ // }
+ //}
+
+ zero_v3(bbd->wanted_co);
+ bbd->wanted_speed = 0.0f;
+
+ /* create random seed for every particle & frame */
+ rand = (int)(psys_frand(psys, psys->seed + p) * 1000);
+ rand = (int)(psys_frand(psys, (int)bbd->cfra + rand) * 1000);
+
+ set_boid_values(&val, bbd->part->boids, pa);
+
+ /* go through rules */
+ switch (state->ruleset_type) {
+ case eBoidRulesetType_Fuzzy:
+ {
+ for (rule = state->rules.first; rule; rule = rule->next) {
+ if (apply_boid_rule(bbd, rule, &val, pa, state->rule_fuzziness))
+ break; /* only first nonzero rule that comes through fuzzy rule is applied */
+ }
+ break;
+ }
+ case eBoidRulesetType_Random:
+ {
+ /* use random rule for each particle (always same for same particle though) */
+ const int n = BLI_listbase_count(&state->rules);
+ if (n) {
+ rule = BLI_findlink(&state->rules, rand % n);
+ apply_boid_rule(bbd, rule, &val, pa, -1.0);
+ }
+ break;
+ }
+ case eBoidRulesetType_Average:
+ {
+ float wanted_co[3] = {0.0f, 0.0f, 0.0f}, wanted_speed = 0.0f;
+ int n = 0;
+ for (rule = state->rules.first; rule; rule=rule->next) {
+ if (apply_boid_rule(bbd, rule, &val, pa, -1.0f)) {
+ add_v3_v3(wanted_co, bbd->wanted_co);
+ wanted_speed += bbd->wanted_speed;
+ n++;
+ zero_v3(bbd->wanted_co);
+ bbd->wanted_speed = 0.0f;
+ }
+ }
+
+ if (n > 1) {
+ mul_v3_fl(wanted_co, 1.0f/(float)n);
+ wanted_speed /= (float)n;
+ }
+
+ copy_v3_v3(bbd->wanted_co, wanted_co);
+ bbd->wanted_speed = wanted_speed;
+ break;
+ }
+
+ }
+
+ /* decide on jumping & liftoff */
+ if (bpa->data.mode == eBoidMode_OnLand) {
+ /* fuzziness makes boids capable of misjudgement */
+ float mul = 1.0f + state->rule_fuzziness;
+
+ if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) {
+ float cvel[3], dir[3];
+
+ copy_v3_v3(dir, pa->prev_state.ave);
+ normalize_v2(dir);
+
+ copy_v3_v3(cvel, bbd->wanted_co);
+ normalize_v2(cvel);
+
+ if (dot_v2v2(cvel, dir) > 0.95f / mul)
+ bpa->data.mode = eBoidMode_Liftoff;
+ }
+ else if (val.jump_speed > 0.0f) {
+ float jump_v[3];
+ int jump = 0;
+
+ /* jump to get to a location */
+ if (bbd->wanted_co[2] > 0.0f) {
+ float cvel[3], dir[3];
+ float z_v, ground_v, cur_v;
+ float len;
+
+ copy_v3_v3(dir, pa->prev_state.ave);
+ normalize_v2(dir);
+
+ copy_v3_v3(cvel, bbd->wanted_co);
+ normalize_v2(cvel);
+
+ len = len_v2(pa->prev_state.vel);
+
+ /* first of all, are we going in a suitable direction? */
+ /* or at a suitably slow speed */
+ if (dot_v2v2(cvel, dir) > 0.95f / mul || len <= state->rule_fuzziness) {
+ /* try to reach goal at highest point of the parabolic path */
+ cur_v = len_v2(pa->prev_state.vel);
+ z_v = sasqrt(-2.0f * bbd->sim->scene->physics_settings.gravity[2] * bbd->wanted_co[2]);
+ ground_v = len_v2(bbd->wanted_co)*sasqrt(-0.5f * bbd->sim->scene->physics_settings.gravity[2] / bbd->wanted_co[2]);
+
+ len = sasqrt((ground_v-cur_v)*(ground_v-cur_v) + z_v*z_v);
+
+ if (len < val.jump_speed * mul || bbd->part->boids->options & BOID_ALLOW_FLIGHT) {
+ jump = 1;
+
+ len = MIN2(len, val.jump_speed);
+
+ copy_v3_v3(jump_v, dir);
+ jump_v[2] = z_v;
+ mul_v3_fl(jump_v, ground_v);
+
+ normalize_v3(jump_v);
+ mul_v3_fl(jump_v, len);
+ add_v2_v2v2(jump_v, jump_v, pa->prev_state.vel);
+ }
+ }
+ }
+
+ /* jump to go faster */
+ if (jump == 0 && val.jump_speed > val.max_speed && bbd->wanted_speed > val.max_speed) {
+
+ }
+
+ if (jump) {
+ copy_v3_v3(pa->prev_state.vel, jump_v);
+ bpa->data.mode = eBoidMode_Falling;
+ }
+ }
+ }
+}
+/* tries to realize the wanted velocity taking all constraints into account */
+void boid_body(BoidBrainData *bbd, ParticleData *pa)
+{
+ BoidSettings *boids = bbd->part->boids;
+ BoidParticle *bpa = pa->boid;
+ BoidValues val;
+ EffectedPoint epoint;
+ float acc[3] = {0.0f, 0.0f, 0.0f}, tan_acc[3], nor_acc[3];
+ float dvec[3], bvec[3];
+ float new_dir[3], new_speed;
+ float old_dir[3], old_speed;
+ float wanted_dir[3];
+ float q[4], mat[3][3]; /* rotation */
+ float ground_co[3] = {0.0f, 0.0f, 0.0f}, ground_nor[3] = {0.0f, 0.0f, 1.0f};
+ float force[3] = {0.0f, 0.0f, 0.0f};
+ float pa_mass=bbd->part->mass, dtime=bbd->dfra*bbd->timestep;
+
+ set_boid_values(&val, boids, pa);
+
+ /* make sure there's something in new velocity, location & rotation */
+ copy_particle_key(&pa->state, &pa->prev_state, 0);
+
+ if (bbd->part->flag & PART_SIZEMASS)
+ pa_mass*=pa->size;
+
+ /* if boids can't fly they fall to the ground */
+ if ((boids->options & BOID_ALLOW_FLIGHT)==0 && ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)==0 && psys_uses_gravity(bbd->sim))
+ bpa->data.mode = eBoidMode_Falling;
+
+ if (bpa->data.mode == eBoidMode_Falling) {
+ /* Falling boids are only effected by gravity. */
+ acc[2] = bbd->sim->scene->physics_settings.gravity[2];
+ }
+ else {
+ /* figure out acceleration */
+ float landing_level = 2.0f;
+ float level = landing_level + 1.0f;
+ float new_vel[3];
+
+ if (bpa->data.mode == eBoidMode_Liftoff) {
+ bpa->data.mode = eBoidMode_InAir;
+ bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
+ }
+ else if (bpa->data.mode == eBoidMode_InAir && boids->options & BOID_ALLOW_LAND) {
+ /* auto-leveling & landing if close to ground */
+
+ bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
+
+ /* level = how many particle sizes above ground */
+ level = (pa->prev_state.co[2] - ground_co[2])/(2.0f * pa->size) - 0.5f;
+
+ landing_level = - boids->landing_smoothness * pa->prev_state.vel[2] * pa_mass;
+
+ if (pa->prev_state.vel[2] < 0.0f) {
+ if (level < 1.0f) {
+ bbd->wanted_co[0] = bbd->wanted_co[1] = bbd->wanted_co[2] = 0.0f;
+ bbd->wanted_speed = 0.0f;
+ bpa->data.mode = eBoidMode_Falling;
+ }
+ else if (level < landing_level) {
+ bbd->wanted_speed *= (level - 1.0f)/landing_level;
+ bbd->wanted_co[2] *= (level - 1.0f)/landing_level;
+ }
+ }
+ }
+
+ copy_v3_v3(old_dir, pa->prev_state.ave);
+ new_speed = normalize_v3_v3(wanted_dir, bbd->wanted_co);
+
+ /* first check if we have valid direction we want to go towards */
+ if (new_speed == 0.0f) {
+ copy_v3_v3(new_dir, old_dir);
+ }
+ else {
+ float old_dir2[2], wanted_dir2[2], nor[3], angle;
+ copy_v2_v2(old_dir2, old_dir);
+ normalize_v2(old_dir2);
+ copy_v2_v2(wanted_dir2, wanted_dir);
+ normalize_v2(wanted_dir2);
+
+ /* choose random direction to turn if wanted velocity */
+ /* is directly behind regardless of z-coordinate */
+ if (dot_v2v2(old_dir2, wanted_dir2) < -0.99f) {
+ wanted_dir[0] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+ wanted_dir[1] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+ wanted_dir[2] = 2.0f*(0.5f - BLI_rng_get_float(bbd->rng));
+ normalize_v3(wanted_dir);
+ }
+
+ /* constrain direction with maximum angular velocity */
+ angle = saacos(dot_v3v3(old_dir, wanted_dir));
+ angle = min_ff(angle, val.max_ave);
+
+ cross_v3_v3v3(nor, old_dir, wanted_dir);
+ axis_angle_to_quat(q, nor, angle);
+ copy_v3_v3(new_dir, old_dir);
+ mul_qt_v3(q, new_dir);
+ normalize_v3(new_dir);
+
+ /* save direction in case resulting velocity too small */
+ axis_angle_to_quat(q, nor, angle*dtime);
+ copy_v3_v3(pa->state.ave, old_dir);
+ mul_qt_v3(q, pa->state.ave);
+ normalize_v3(pa->state.ave);
+ }
+
+ /* constrain speed with maximum acceleration */
+ old_speed = len_v3(pa->prev_state.vel);
+
+ if (bbd->wanted_speed < old_speed)
+ new_speed = MAX2(bbd->wanted_speed, old_speed - val.max_acc);
+ else
+ new_speed = MIN2(bbd->wanted_speed, old_speed + val.max_acc);
+
+ /* combine direction and speed */
+ copy_v3_v3(new_vel, new_dir);
+ mul_v3_fl(new_vel, new_speed);
+
+ /* maintain minimum flying velocity if not landing */
+ if (level >= landing_level) {
+ float len2 = dot_v2v2(new_vel, new_vel);
+ float root;
+
+ len2 = MAX2(len2, val.min_speed*val.min_speed);
+ root = sasqrt(new_speed*new_speed - len2);
+
+ new_vel[2] = new_vel[2] < 0.0f ? -root : root;
+
+ normalize_v2(new_vel);
+ mul_v2_fl(new_vel, sasqrt(len2));
+ }
+
+ /* finally constrain speed to max speed */
+ new_speed = normalize_v3(new_vel);
+ mul_v3_fl(new_vel, MIN2(new_speed, val.max_speed));
+
+ /* get acceleration from difference of velocities */
+ sub_v3_v3v3(acc, new_vel, pa->prev_state.vel);
+
+ /* break acceleration to components */
+ project_v3_v3v3(tan_acc, acc, pa->prev_state.ave);
+ sub_v3_v3v3(nor_acc, acc, tan_acc);
+ }
+
+ /* account for effectors */
+ pd_point_from_particle(bbd->sim, pa, &pa->state, &epoint);
+ pdDoEffectors(bbd->sim->psys->effectors, bbd->sim->colliders, bbd->part->effector_weights, &epoint, force, NULL);
+
+ if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing)) {
+ float length = normalize_v3(force);
+
+ length = MAX2(0.0f, length - boids->land_stick_force);
+
+ mul_v3_fl(force, length);
+ }
+
+ add_v3_v3(acc, force);
+
+ /* store smoothed acceleration for nice banking etc. */
+ madd_v3_v3fl(bpa->data.acc, acc, dtime);
+ mul_v3_fl(bpa->data.acc, 1.0f / (1.0f + dtime));
+
+ /* integrate new location & velocity */
+
+ /* by regarding the acceleration as a force at this stage we*/
+ /* can get better control allthough it's a bit unphysical */
+ mul_v3_fl(acc, 1.0f/pa_mass);
+
+ copy_v3_v3(dvec, acc);
+ mul_v3_fl(dvec, dtime*dtime*0.5f);
+
+ copy_v3_v3(bvec, pa->prev_state.vel);
+ mul_v3_fl(bvec, dtime);
+ add_v3_v3(dvec, bvec);
+ add_v3_v3(pa->state.co, dvec);
+
+ madd_v3_v3fl(pa->state.vel, acc, dtime);
+
+ //if (bpa->data.mode != eBoidMode_InAir)
+ bpa->ground = boid_find_ground(bbd, pa, ground_co, ground_nor);
+
+ /* change modes, constrain movement & keep track of down vector */
+ switch (bpa->data.mode) {
+ case eBoidMode_InAir:
+ {
+ float grav[3];
+
+ grav[0] = 0.0f;
+ grav[1] = 0.0f;
+ grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f;
+
+ /* don't take forward acceleration into account (better banking) */
+ if (dot_v3v3(bpa->data.acc, pa->state.vel) > 0.0f) {
+ project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel);
+ sub_v3_v3v3(dvec, bpa->data.acc, dvec);
+ }
+ else {
+ copy_v3_v3(dvec, bpa->data.acc);
+ }
+
+ /* gather apparent gravity */
+ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking);
+ normalize_v3(bpa->gravity);
+
+ /* stick boid on goal when close enough */
+ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) {
+ bpa->data.mode = eBoidMode_Climbing;
+ bpa->ground = bbd->goal_ob;
+ boid_find_ground(bbd, pa, ground_co, ground_nor);
+ boid_climb(boids, pa, ground_co, ground_nor);
+ }
+ else if (pa->state.co[2] <= ground_co[2] + pa->size * boids->height) {
+ /* land boid when below ground */
+ if (boids->options & BOID_ALLOW_LAND) {
+ pa->state.co[2] = ground_co[2] + pa->size * boids->height;
+ pa->state.vel[2] = 0.0f;
+ bpa->data.mode = eBoidMode_OnLand;
+ }
+ /* fly above ground */
+ else if (bpa->ground) {
+ pa->state.co[2] = ground_co[2] + pa->size * boids->height;
+ pa->state.vel[2] = 0.0f;
+ }
+ }
+ break;
+ }
+ case eBoidMode_Falling:
+ {
+ float grav[3];
+
+ grav[0] = 0.0f;
+ grav[1] = 0.0f;
+ grav[2] = bbd->sim->scene->physics_settings.gravity[2] < 0.0f ? -1.0f : 0.0f;
+
+
+ /* gather apparent gravity */
+ madd_v3_v3fl(bpa->gravity, grav, dtime);
+ normalize_v3(bpa->gravity);
+
+ if (boids->options & BOID_ALLOW_LAND) {
+ /* stick boid on goal when close enough */
+ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) {
+ bpa->data.mode = eBoidMode_Climbing;
+ bpa->ground = bbd->goal_ob;
+ boid_find_ground(bbd, pa, ground_co, ground_nor);
+ boid_climb(boids, pa, ground_co, ground_nor);
+ }
+ /* land boid when really near ground */
+ else if (pa->state.co[2] <= ground_co[2] + 1.01f * pa->size * boids->height) {
+ pa->state.co[2] = ground_co[2] + pa->size * boids->height;
+ pa->state.vel[2] = 0.0f;
+ bpa->data.mode = eBoidMode_OnLand;
+ }
+ /* if we're falling, can fly and want to go upwards lets fly */
+ else if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f)
+ bpa->data.mode = eBoidMode_InAir;
+ }
+ else
+ bpa->data.mode = eBoidMode_InAir;
+ break;
+ }
+ case eBoidMode_Climbing:
+ {
+ boid_climb(boids, pa, ground_co, ground_nor);
+ //float nor[3];
+ //copy_v3_v3(nor, ground_nor);
+
+ ///* gather apparent gravity to r_ve */
+ //madd_v3_v3fl(pa->r_ve, ground_nor, -1.0);
+ //normalize_v3(pa->r_ve);
+
+ ///* raise boid it's size from surface */
+ //mul_v3_fl(nor, pa->size * boids->height);
+ //add_v3_v3v3(pa->state.co, ground_co, nor);
+
+ ///* remove normal component from velocity */
+ //project_v3_v3v3(v, pa->state.vel, ground_nor);
+ //sub_v3_v3v3(pa->state.vel, pa->state.vel, v);
+ break;
+ }
+ case eBoidMode_OnLand:
+ {
+ /* stick boid on goal when close enough */
+ if (bbd->goal_ob && boid_goal_signed_dist(pa->state.co, bbd->goal_co, bbd->goal_nor) <= pa->size * boids->height) {
+ bpa->data.mode = eBoidMode_Climbing;
+ bpa->ground = bbd->goal_ob;
+ boid_find_ground(bbd, pa, ground_co, ground_nor);
+ boid_climb(boids, pa, ground_co, ground_nor);
+ }
+ /* ground is too far away so boid falls */
+ else if (pa->state.co[2]-ground_co[2] > 1.1f * pa->size * boids->height)
+ bpa->data.mode = eBoidMode_Falling;
+ else {
+ /* constrain to surface */
+ pa->state.co[2] = ground_co[2] + pa->size * boids->height;
+ pa->state.vel[2] = 0.0f;
+ }
+
+ if (boids->banking > 0.0f) {
+ float grav[3];
+ /* Don't take gravity's strength in to account, */
+ /* otherwise amount of banking is hard to control. */
+ negate_v3_v3(grav, ground_nor);
+
+ project_v3_v3v3(dvec, bpa->data.acc, pa->state.vel);
+ sub_v3_v3v3(dvec, bpa->data.acc, dvec);
+
+ /* gather apparent gravity */
+ madd_v3_v3v3fl(bpa->gravity, grav, dvec, -boids->banking);
+ normalize_v3(bpa->gravity);
+ }
+ else {
+ /* gather negative surface normal */
+ madd_v3_v3fl(bpa->gravity, ground_nor, -1.0f);
+ normalize_v3(bpa->gravity);
+ }
+ break;
+ }
+ }
+
+ /* save direction to state.ave unless the boid is falling */
+ /* (boids can't effect their direction when falling) */
+ if (bpa->data.mode!=eBoidMode_Falling && len_v3(pa->state.vel) > 0.1f*pa->size) {
+ copy_v3_v3(pa->state.ave, pa->state.vel);
+ pa->state.ave[2] *= bbd->part->boids->pitch;
+ normalize_v3(pa->state.ave);
+ }
+
+ /* apply damping */
+ if (ELEM(bpa->data.mode, eBoidMode_OnLand, eBoidMode_Climbing))
+ mul_v3_fl(pa->state.vel, 1.0f - 0.2f*bbd->part->dampfac);
+
+ /* calculate rotation matrix based on forward & down vectors */
+ if (bpa->data.mode == eBoidMode_InAir) {
+ copy_v3_v3(mat[0], pa->state.ave);
+
+ project_v3_v3v3(dvec, bpa->gravity, pa->state.ave);
+ sub_v3_v3v3(mat[2], bpa->gravity, dvec);
+ normalize_v3(mat[2]);
+ }
+ else {
+ project_v3_v3v3(dvec, pa->state.ave, bpa->gravity);
+ sub_v3_v3v3(mat[0], pa->state.ave, dvec);
+ normalize_v3(mat[0]);
+
+ copy_v3_v3(mat[2], bpa->gravity);
+ }
+ negate_v3(mat[2]);
+ cross_v3_v3v3(mat[1], mat[2], mat[0]);
+
+ /* apply rotation */
+ mat3_to_quat_is_ok(q, mat);
+ copy_qt_qt(pa->state.rot, q);
+}
+
+BoidRule *boid_new_rule(int type)
+{
+ BoidRule *rule = NULL;
+ if (type <= 0)
+ return NULL;
+
+ switch (type) {
+ case eBoidRuleType_Goal:
+ case eBoidRuleType_Avoid:
+ rule = MEM_callocN(sizeof(BoidRuleGoalAvoid), "BoidRuleGoalAvoid");
+ break;
+ case eBoidRuleType_AvoidCollision:
+ rule = MEM_callocN(sizeof(BoidRuleAvoidCollision), "BoidRuleAvoidCollision");
+ ((BoidRuleAvoidCollision*)rule)->look_ahead = 2.0f;
+ break;
+ case eBoidRuleType_FollowLeader:
+ rule = MEM_callocN(sizeof(BoidRuleFollowLeader), "BoidRuleFollowLeader");
+ ((BoidRuleFollowLeader*)rule)->distance = 1.0f;
+ break;
+ case eBoidRuleType_AverageSpeed:
+ rule = MEM_callocN(sizeof(BoidRuleAverageSpeed), "BoidRuleAverageSpeed");
+ ((BoidRuleAverageSpeed*)rule)->speed = 0.5f;
+ break;
+ case eBoidRuleType_Fight:
+ rule = MEM_callocN(sizeof(BoidRuleFight), "BoidRuleFight");
+ ((BoidRuleFight*)rule)->distance = 100.0f;
+ ((BoidRuleFight*)rule)->flee_distance = 100.0f;
+ break;
+ default:
+ rule = MEM_callocN(sizeof(BoidRule), "BoidRule");
+ break;
+ }
+
+ rule->type = type;
+ rule->flag |= BOIDRULE_IN_AIR|BOIDRULE_ON_LAND;
+ BLI_strncpy(rule->name, rna_enum_boidrule_type_items[type-1].name, sizeof(rule->name));
+
+ return rule;
+}
+void boid_default_settings(BoidSettings *boids)
+{
+ boids->air_max_speed = 10.0f;
+ boids->air_max_acc = 0.5f;
+ boids->air_max_ave = 0.5f;
+ boids->air_personal_space = 1.0f;
+
+ boids->land_max_speed = 5.0f;
+ boids->land_max_acc = 0.5f;
+ boids->land_max_ave = 0.5f;
+ boids->land_personal_space = 1.0f;
+
+ boids->options = BOID_ALLOW_FLIGHT;
+
+ boids->landing_smoothness = 3.0f;
+ boids->banking = 1.0f;
+ boids->pitch = 1.0f;
+ boids->height = 1.0f;
+
+ boids->health = 1.0f;
+ boids->accuracy = 1.0f;
+ boids->aggression = 2.0f;
+ boids->range = 1.0f;
+ boids->strength = 0.1f;
+}
+
+BoidState *boid_new_state(BoidSettings *boids)
+{
+ BoidState *state = MEM_callocN(sizeof(BoidState), "BoidState");
+
+ state->id = boids->last_state_id++;
+ if (state->id)
+ BLI_snprintf(state->name, sizeof(state->name), "State %i", state->id);
+ else
+ strcpy(state->name, "State");
+
+ state->rule_fuzziness = 0.5;
+ state->volume = 1.0f;
+ state->channels |= ~0;
+
+ return state;
+}
+
+BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state)
+{
+ BoidState *staten = MEM_dupallocN(state);
+
+ BLI_duplicatelist(&staten->rules, &state->rules);
+ BLI_duplicatelist(&staten->conditions, &state->conditions);
+ BLI_duplicatelist(&staten->actions, &state->actions);
+
+ staten->id = boids->last_state_id++;
+
+ return staten;
+}
+void boid_free_settings(BoidSettings *boids)
+{
+ if (boids) {
+ BoidState *state = boids->states.first;
+
+ for (; state; state=state->next) {
+ BLI_freelistN(&state->rules);
+ BLI_freelistN(&state->conditions);
+ BLI_freelistN(&state->actions);
+ }
+
+ BLI_freelistN(&boids->states);
+
+ MEM_freeN(boids);
+ }
+}
+BoidSettings *boid_copy_settings(BoidSettings *boids)
+{
+ BoidSettings *nboids = NULL;
+
+ if (boids) {
+ BoidState *state;
+ BoidState *nstate;
+
+ nboids = MEM_dupallocN(boids);
+
+ BLI_duplicatelist(&nboids->states, &boids->states);
+
+ state = boids->states.first;
+ nstate = nboids->states.first;
+ for (; state; state=state->next, nstate=nstate->next) {
+ BLI_duplicatelist(&nstate->rules, &state->rules);
+ BLI_duplicatelist(&nstate->conditions, &state->conditions);
+ BLI_duplicatelist(&nstate->actions, &state->actions);
+ }
+ }
+
+ return nboids;
+}
+BoidState *boid_get_current_state(BoidSettings *boids)
+{
+ BoidState *state = boids->states.first;
+
+ for (; state; state=state->next) {
+ if (state->flag & BOIDSTATE_CURRENT)
+ break;
+ }
+
+ return state;
+}
+
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 4121bde4d0b..487b8ffa2b5 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -56,6 +56,7 @@
#include "DNA_object_fluidsim.h"
#include "DNA_object_force.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_sequence_types.h"
#include "DNA_sound_types.h"
#include "DNA_text_types.h"
@@ -462,6 +463,20 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
{
Object *ob = (Object *)id;
ModifierData *md;
+ ParticleSystem *psys;
+
+#define BPATH_TRAVERSE_POINTCACHE(ptcaches) \
+ { \
+ PointCache *cache; \
+ for (cache = (ptcaches).first; cache; cache = cache->next) { \
+ if (cache->flag & PTCACHE_DISK_CACHE) { \
+ rewrite_path_fixed(cache->path, \
+ visit_cb, \
+ absbase, \
+ bpath_user_data); \
+ } \
+ } \
+ } (void)0
/* do via modifiers instead */
#if 0
@@ -477,6 +492,16 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data);
}
}
+ else if (md->type == eModifierType_Smoke) {
+ SmokeModifierData *smd = (SmokeModifierData *)md;
+ if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
+ BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]);
+ }
+ }
+ else if (md->type == eModifierType_Cloth) {
+ ClothModifierData *clmd = (ClothModifierData *) md;
+ BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
+ }
else if (md->type == eModifierType_Ocean) {
OceanModifierData *omd = (OceanModifierData *) md;
rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
@@ -487,6 +512,16 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
}
}
+ if (ob->soft) {
+ BPATH_TRAVERSE_POINTCACHE(ob->soft->ptcaches);
+ }
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ BPATH_TRAVERSE_POINTCACHE(psys->ptcaches);
+ }
+
+#undef BPATH_TRAVERSE_POINTCACHE
+
break;
}
case ID_SO:
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 87733341cdd..162525c7cd5 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -45,6 +45,7 @@
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_modifier.h"
+#include "BKE_pointcache.h"
#include "BPH_mass_spring.h"
@@ -130,6 +131,9 @@ void cloth_init(ClothModifierData *clmd )
if (!clmd->sim_parms->effector_weights)
clmd->sim_parms->effector_weights = BKE_add_effector_weights(NULL);
+
+ if (clmd->point_cache)
+ clmd->point_cache->step = 1;
}
static BVHTree *bvhselftree_build_from_cloth (ClothModifierData *clmd, float epsilon)
@@ -300,16 +304,35 @@ void bvhselftree_update_from_cloth(ClothModifierData *clmd, bool moving)
}
}
+void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr)
+{
+ PTCacheID pid;
+
+ BKE_ptcache_id_from_cloth(&pid, ob, clmd);
+
+ // don't do anything as long as we're in editmode!
+ if (pid.cache->edit && ob->mode & OB_MODE_PARTICLE_EDIT)
+ return;
+
+ BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_AFTER, framenr);
+}
+
static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *result, int framenr)
{
+ PointCache *cache;
+
+ cache= clmd->point_cache;
+
/* initialize simulation data if it didn't exist already */
if (clmd->clothObject == NULL) {
if (!cloth_from_object(ob, clmd, result, framenr, 1)) {
+ BKE_ptcache_invalidate(cache);
modifier_setError(&(clmd->modifier), "Can't initialize cloth");
return 0;
}
if (clmd->clothObject == NULL) {
+ BKE_ptcache_invalidate(cache);
modifier_setError(&(clmd->modifier), "Null cloth object");
return 0;
}
@@ -347,7 +370,7 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
mul_m4_v3(ob->obmat, verts->xconst);
}
- effectors = pdInitEffectors(clmd->scene, ob, clmd->sim_parms->effector_weights, true);
+ effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights, true);
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH )
cloth_update_verts ( ob, clmd, result );
@@ -379,14 +402,27 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
************************************************/
void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, DerivedMesh *dm, float (*vertexCos)[3])
{
- int framenr = scene->r.cfra, startframe = scene->r.sfra, endframe = scene->r.efra;
+ PointCache *cache;
+ PTCacheID pid;
+ float timescale;
+ int framenr, startframe, endframe;
+ int cache_result;
clmd->scene= scene; /* nice to pass on later :) */
+ framenr= (int)scene->r.cfra;
+ cache= clmd->point_cache;
- clmd->sim_parms->timescale = 1.0f;
+ BKE_ptcache_id_from_cloth(&pid, ob, clmd);
+ BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, &timescale);
+ clmd->sim_parms->timescale= timescale * clmd->sim_parms->time_scale;
if (clmd->sim_parms->reset || (clmd->clothObject && dm->getNumVerts(dm) != clmd->clothObject->mvert_num)) {
clmd->sim_parms->reset = 0;
+ cache->flag |= PTCACHE_OUTDATED;
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
+ BKE_ptcache_validate(cache, 0);
+ cache->last_exact= 0;
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
}
// unused in the moment, calculated separately in implicit.c
@@ -394,6 +430,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
/* simulation is only active during a specific period */
if (framenr < startframe) {
+ BKE_ptcache_invalidate(cache);
return;
}
else if (framenr > endframe) {
@@ -405,18 +442,58 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
return;
if (framenr == startframe) {
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
do_init_cloth(ob, clmd, dm, framenr);
+ BKE_ptcache_validate(cache, framenr);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
clmd->clothObject->last_frame= framenr;
return;
}
- if (framenr!=clmd->clothObject->last_frame+1)
+ /* try to read from cache */
+ bool can_simulate = (framenr == clmd->clothObject->last_frame+1) && !(cache->flag & PTCACHE_BAKED);
+
+ cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate);
+
+ if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED ||
+ (!can_simulate && cache_result == PTCACHE_READ_OLD)) {
+ BKE_cloth_solver_set_positions(clmd);
+ cloth_to_object (ob, clmd, vertexCos);
+
+ BKE_ptcache_validate(cache, framenr);
+
+ if (cache_result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED)
+ BKE_ptcache_write(&pid, framenr);
+
+ clmd->clothObject->last_frame= framenr;
+
+ return;
+ }
+ else if (cache_result==PTCACHE_READ_OLD) {
+ BKE_cloth_solver_set_positions(clmd);
+ }
+ else if ( /*ob->id.lib ||*/ (cache->flag & PTCACHE_BAKED)) { /* 2.4x disabled lib, but this can be used in some cases, testing further - campbell */
+ /* if baked and nothing in cache, do nothing */
+ BKE_ptcache_invalidate(cache);
return;
+ }
+
+ return;
+
+ /* if on second frame, write cache for first frame */
+ if (cache->simframe == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0))
+ BKE_ptcache_write(&pid, startframe);
- clmd->sim_parms->timescale *= 1.0f;
+ clmd->sim_parms->timescale *= framenr - cache->simframe;
/* do simulation */
- do_step_cloth(ob, clmd, dm, framenr);
+ BKE_ptcache_validate(cache, framenr);
+
+ if (!do_step_cloth(ob, clmd, dm, framenr)) {
+ BKE_ptcache_invalidate(cache);
+ }
+ else
+ BKE_ptcache_write(&pid, framenr);
cloth_to_object (ob, clmd, vertexCos);
clmd->clothObject->last_frame= framenr;
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index b38f59a3723..9d927d04b2a 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -927,6 +927,7 @@ int CTX_data_mode_enum(const bContext *C)
else if (ob->mode & OB_MODE_WEIGHT_PAINT) return CTX_MODE_PAINT_WEIGHT;
else if (ob->mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX;
else if (ob->mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE;
+ else if (ob->mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE;
}
}
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 475afb9a571..a8341939692 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -54,8 +54,6 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
-#include "DNA_object_types.h"
-#include "DNA_object_force.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
@@ -83,6 +81,8 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
@@ -478,7 +478,7 @@ void dag_add_collision_relations(DagForest *dag, Scene *scene, Object *ob, DagNo
void dag_add_forcefield_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name)
{
- ListBase *effectors = pdInitEffectors(scene, ob, effector_weights, false);
+ ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
if (effectors) {
for (EffectorCache *eff = effectors->first; eff; eff = eff->next) {
@@ -507,6 +507,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
DagNode *node2;
DagNode *node3;
Key *key;
+ ParticleSystem *psys;
int addtoroot = 1;
node = dag_get_node(dag, ob);
@@ -748,6 +749,83 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
dag_add_lamp_driver_relations(dag, node, ob->data);
}
+ /* particles */
+ psys = ob->particlesystem.first;
+ if (psys) {
+ GroupObject *go;
+
+ for (; psys; psys = psys->next) {
+ BoidRule *rule = NULL;
+ BoidState *state = NULL;
+ ParticleSettings *part = psys->part;
+
+ if (part->adt) {
+ dag_add_driver_relation(part->adt, dag, node, 1);
+ }
+
+ dag_add_relation(dag, node, node, DAG_RL_OB_DATA, "Particle-Object Relation");
+
+ if (!psys_check_enabled(ob, psys, G.is_rendering))
+ continue;
+
+ if (ELEM(part->phystype, PART_PHYS_KEYED, PART_PHYS_BOIDS)) {
+ ParticleTarget *pt = psys->targets.first;
+
+ for (; pt; pt = pt->next) {
+ if (pt->ob && BLI_findlink(&pt->ob->particlesystem, pt->psys - 1)) {
+ node2 = dag_get_node(dag, pt->ob);
+ dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Targets");
+ }
+ }
+ }
+
+ if (part->ren_as == PART_DRAW_OB && part->dup_ob) {
+ node2 = dag_get_node(dag, part->dup_ob);
+ /* note that this relation actually runs in the wrong direction, the problem
+ * is that dupli system all have this (due to parenting), and the render
+ * engine instancing assumes particular ordering of objects in list */
+ dag_add_relation(dag, node, node2, DAG_RL_OB_OB, "Particle Object Visualization");
+ if (part->dup_ob->type == OB_MBALL)
+ dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Object Visualization");
+ }
+
+ if (part->ren_as == PART_DRAW_GR && part->dup_group) {
+ for (go = part->dup_group->gobject.first; go; go = go->next) {
+ node2 = dag_get_node(dag, go->ob);
+ dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Particle Group Visualization");
+ }
+ }
+
+ if (part->type != PART_HAIR) {
+ /* Actual code uses get_collider_cache */
+ dag_add_collision_relations(dag, scene, ob, node, part->collision_group, ob->lay, eModifierType_Collision, NULL, true, "Particle Collision");
+ }
+ else if ((psys->flag & PSYS_HAIR_DYNAMICS) && psys->clmd && psys->clmd->coll_parms) {
+ /* Hair uses cloth simulation, i.e. get_collision_objects */
+ dag_add_collision_relations(dag, scene, ob, node, psys->clmd->coll_parms->group, ob->lay | scene->lay, eModifierType_Collision, NULL, true, "Hair Collision");
+ }
+
+ dag_add_forcefield_relations(dag, scene, ob, node, part->effector_weights, part->type == PART_HAIR, 0, "Particle Force Field");
+
+ if (part->boids) {
+ for (state = part->boids->states.first; state; state = state->next) {
+ for (rule = state->rules.first; rule; rule = rule->next) {
+ Object *ruleob = NULL;
+ if (rule->type == eBoidRuleType_Avoid)
+ ruleob = ((BoidRuleGoalAvoid *)rule)->ob;
+ else if (rule->type == eBoidRuleType_FollowLeader)
+ ruleob = ((BoidRuleFollowLeader *)rule)->ob;
+
+ if (ruleob) {
+ node2 = dag_get_node(dag, ruleob);
+ dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Boid Rule");
+ }
+ }
+ }
+ }
+ }
+ }
+
/* object constraints */
for (con = ob->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
@@ -1817,6 +1895,38 @@ static unsigned int flush_layer_node(Scene *sce, DagNode *node, int curtime)
return node->lay;
}
+/* node was checked to have lasttime != curtime, and is of type ID_OB */
+static void flush_pointcache_reset(Main *bmain, Scene *scene, DagNode *node,
+ int curtime, unsigned int lay, bool reset)
+{
+ DagAdjList *itA;
+ Object *ob;
+
+ node->lasttime = curtime;
+
+ for (itA = node->child; itA; itA = itA->next) {
+ if (itA->node->type == ID_OB) {
+ if (itA->node->lasttime != curtime) {
+ ob = (Object *)(itA->node->ob);
+
+ if (reset || (ob->recalc & OB_RECALC_ALL)) {
+ if (BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_DEPSGRAPH)) {
+ /* Don't tag nodes which are on invisible layer. */
+ if (itA->node->lay & lay) {
+ ob->recalc |= OB_RECALC_DATA;
+ lib_id_recalc_data_tag(bmain, &ob->id);
+ }
+ }
+
+ flush_pointcache_reset(bmain, scene, itA->node, curtime, lay, true);
+ }
+ else
+ flush_pointcache_reset(bmain, scene, itA->node, curtime, lay, false);
+ }
+ }
+ }
+}
+
/* flush layer flags to dependencies */
static void dag_scene_flush_layers(Scene *sce, int lay)
{
@@ -1894,6 +2004,7 @@ void DAG_scene_flush_update(Main *bmain, Scene *sce, unsigned int lay, const sho
{
DagNode *firstnode;
DagAdjList *itA;
+ Object *ob;
int lasttime;
if (!DEG_depsgraph_use_legacy()) {
@@ -1921,6 +2032,24 @@ void DAG_scene_flush_update(Main *bmain, Scene *sce, unsigned int lay, const sho
if (!time) {
sce->theDag->time++; /* so we know which nodes were accessed */
lasttime = sce->theDag->time;
+ for (itA = firstnode->child; itA; itA = itA->next) {
+ if (itA->node->lasttime != lasttime && itA->node->type == ID_OB) {
+ ob = (Object *)(itA->node->ob);
+
+ if (ob->recalc & OB_RECALC_ALL) {
+ if (BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH)) {
+ ob->recalc |= OB_RECALC_DATA;
+ lib_id_recalc_data_tag(bmain, &ob->id);
+ }
+
+ flush_pointcache_reset(bmain, sce, itA->node, lasttime,
+ lay, true);
+ }
+ else
+ flush_pointcache_reset(bmain, sce, itA->node, lasttime,
+ lay, false);
+ }
+ }
}
dag_tag_renderlayers(sce, lay);
@@ -2113,6 +2242,8 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob)
ob->recalc |= OB_RECALC_DATA;
}
}
+ if (ob->particlesystem.first)
+ ob->recalc |= OB_RECALC_DATA;
break;
case OB_CURVE:
case OB_SURF:
@@ -2151,6 +2282,17 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob)
ob->recalc |= OB_RECALC_DATA;
adt->recalc |= ADT_RECALC_ANIM;
}
+
+ if (ob->particlesystem.first) {
+ ParticleSystem *psys = ob->particlesystem.first;
+
+ for (; psys; psys = psys->next) {
+ if (psys_check_enabled(ob, psys, G.is_rendering)) {
+ ob->recalc |= OB_RECALC_DATA;
+ break;
+ }
+ }
+ }
}
if (ob->recalc & OB_RECALC_OB)
@@ -2442,6 +2584,7 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
/* set flags & pointcache for object */
if (GS(id->name) == ID_OB) {
ob = (Object *)id;
+ BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH);
/* So if someone tagged object recalc directly,
* id_tag_update bit-field stays relevant
@@ -2472,6 +2615,7 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
if (!(ob && obt == ob) && obt->data == id) {
obt->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obt->id);
+ BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
}
}
@@ -2499,6 +2643,30 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
obt->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obt->id);
}
+
+ /* particle settings can use the texture as well */
+ if (obt->particlesystem.first) {
+ ParticleSystem *psys = obt->particlesystem.first;
+ MTex **mtexp, *mtex;
+ int a;
+ for (; psys; psys = psys->next) {
+ mtexp = psys->part->mtex;
+ for (a = 0; a < MAX_MTEX; a++, mtexp++) {
+ mtex = *mtexp;
+ if (mtex && mtex->tex == (Tex *)id) {
+ obt->recalc |= OB_RECALC_DATA;
+ lib_id_recalc_data_tag(bmain, &obt->id);
+
+ if (mtex->mapto & PAMAP_INIT)
+ psys->recalc |= PSYS_RECALC_RESET;
+ if (mtex->mapto & PAMAP_CHILD)
+ psys->recalc |= PSYS_RECALC_CHILD;
+
+ BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
+ }
+ }
+ }
+ }
}
}
@@ -2510,10 +2678,20 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA);
lib_id_recalc_tag(bmain, &obt->id);
lib_id_recalc_data_tag(bmain, &obt->id);
+ BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
}
}
+ /* set flags based on particle settings */
+ if (idtype == ID_PA) {
+ ParticleSystem *psys;
+ for (obt = bmain->object.first; obt; obt = obt->id.next)
+ for (psys = obt->particlesystem.first; psys; psys = psys->next)
+ if (&psys->part->id == id)
+ BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
+ }
+
if (ELEM(idtype, ID_MA, ID_TE)) {
obt = sce->basact ? sce->basact->object : NULL;
if (obt && obt->mode & OB_MODE_TEXTURE_PAINT) {
@@ -2783,7 +2961,7 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
if (flag) {
if (flag & OB_RECALC_OB)
lib_id_recalc_tag(bmain, id);
- if (flag & OB_RECALC_DATA)
+ if (flag & (OB_RECALC_DATA | PSYS_RECALC))
lib_id_recalc_data_tag(bmain, id);
}
else
@@ -2799,6 +2977,20 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
ob = (Object *)id;
ob->recalc |= (flag & OB_RECALC_ALL);
}
+ else if (idtype == ID_PA) {
+ ParticleSystem *psys;
+ /* this is weak still, should be done delayed as well */
+ for (ob = bmain->object.first; ob; ob = ob->id.next) {
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (&psys->part->id == id) {
+ ob->recalc |= (flag & OB_RECALC_ALL);
+ psys->recalc |= (flag & PSYS_RECALC);
+ lib_id_recalc_tag(bmain, &ob->id);
+ lib_id_recalc_data_tag(bmain, &ob->id);
+ }
+ }
+ }
+ }
else {
/* disable because this is called on various ID types automatically.
* where printing warning is not useful. for now just ignore */
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 3f5c5cd7bf4..1daf999e4a9 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -48,7 +48,6 @@
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
-#include "DNA_object_force.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
@@ -69,6 +68,8 @@
#include "BKE_mesh_mapping.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
@@ -562,7 +563,7 @@ static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist)
}
/* check whether bounds intersects a point with given radius */
-static bool UNUSED_FUNCTION(boundIntersectPoint)(Bounds3D *b, float point[3], const float radius)
+static bool boundIntersectPoint(Bounds3D *b, float point[3], const float radius)
{
if (!b->valid)
return false;
@@ -861,7 +862,7 @@ static void surface_freeUnusedData(DynamicPaintSurface *surface)
return;
/* free bakedata if not active or surface is baked */
- if (!(surface->flags & MOD_DPAINT_ACTIVE)) {
+ if (!(surface->flags & MOD_DPAINT_ACTIVE) || (surface->pointcache && surface->pointcache->flag & PTCACHE_BAKED)) {
free_bakeData(surface->data);
}
}
@@ -896,6 +897,10 @@ void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface)
void dynamicPaint_freeSurface(DynamicPaintSurface *surface)
{
+ /* point cache */
+ BKE_ptcache_free_list(&(surface->ptcaches));
+ surface->pointcache = NULL;
+
if (surface->effector_weights)
MEM_freeN(surface->effector_weights);
surface->effector_weights = NULL;
@@ -956,6 +961,11 @@ DynamicPaintSurface *dynamicPaint_createNewSurface(DynamicPaintCanvasSettings *c
surface->format = MOD_DPAINT_SURFACE_F_VERTEX;
surface->type = MOD_DPAINT_SURFACE_T_PAINT;
+ /* cache */
+ surface->pointcache = BKE_ptcache_add(&(surface->ptcaches));
+ surface->pointcache->flag |= PTCACHE_DISK_CACHE;
+ surface->pointcache->step = 1;
+
/* Set initial values */
surface->flags = MOD_DPAINT_ANTIALIAS | MOD_DPAINT_MULALPHA | MOD_DPAINT_DRY_LOG | MOD_DPAINT_DISSOLVE_LOG |
MOD_DPAINT_ACTIVE | MOD_DPAINT_PREVIEW | MOD_DPAINT_OUT1 | MOD_DPAINT_USE_DRYING;
@@ -1046,6 +1056,8 @@ bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, str
return false;
brush->pmd = pmd;
+ brush->psys = NULL;
+
brush->flags = MOD_DPAINT_ABS_ALPHA | MOD_DPAINT_RAMP_ALPHA;
brush->collision = MOD_DPAINT_COL_VOLUME;
@@ -1199,6 +1211,7 @@ void dynamicPaint_Modifier_copy(struct DynamicPaintModifierData *pmd, struct Dyn
t_brush->particle_radius = brush->particle_radius;
t_brush->particle_smooth = brush->particle_smooth;
t_brush->paint_distance = brush->paint_distance;
+ t_brush->psys = brush->psys;
if (brush->paint_ramp)
memcpy(t_brush->paint_ramp, brush->paint_ramp, sizeof(ColorBand));
@@ -1931,6 +1944,15 @@ static DerivedMesh *dynamicPaint_Modifier_apply(
return result;
}
+/* update cache frame range */
+void dynamicPaint_cacheUpdateFrames(DynamicPaintSurface *surface)
+{
+ if (surface->pointcache) {
+ surface->pointcache->startframe = surface->start_frame;
+ surface->pointcache->endframe = surface->end_frame;
+ }
+}
+
static void canvas_copyDerivedMesh(DynamicPaintCanvasSettings *canvas, DerivedMesh *dm)
{
if (canvas->dm) {
@@ -1979,10 +2001,31 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene
if (no_surface_data || current_frame != surface->current_frame ||
(int)scene->r.cfra == surface->start_frame)
{
+ PointCache *cache = surface->pointcache;
+ PTCacheID pid;
surface->current_frame = current_frame;
- /* if we're on surface range do recalculate */
- if ((int)scene->r.cfra == current_frame) {
+ /* read point cache */
+ BKE_ptcache_id_from_dynamicpaint(&pid, ob, surface);
+ pid.cache->startframe = surface->start_frame;
+ pid.cache->endframe = surface->end_frame;
+ BKE_ptcache_id_time(&pid, scene, (float)scene->r.cfra, NULL, NULL, NULL);
+
+ /* reset non-baked cache at first frame */
+ if ((int)scene->r.cfra == surface->start_frame && !(cache->flag & PTCACHE_BAKED)) {
+ cache->flag |= PTCACHE_REDO_NEEDED;
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+ }
+
+ /* try to read from cache */
+ bool can_simulate = ((int)scene->r.cfra == current_frame) && !(cache->flag & PTCACHE_BAKED);
+
+ if (BKE_ptcache_read(&pid, (float)scene->r.cfra, can_simulate)) {
+ BKE_ptcache_validate(cache, (int)scene->r.cfra);
+ }
+ /* if read failed and we're on surface range do recalculate */
+ else if (can_simulate) {
/* calculate surface frame */
canvas->flags |= MOD_DPAINT_BAKING;
dynamicPaint_calculateFrame(surface, scene, ob, current_frame);
@@ -1994,6 +2037,9 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene
{
canvas_copyDerivedMesh(canvas, dm);
}
+
+ BKE_ptcache_validate(cache, surface->current_frame);
+ BKE_ptcache_write(&pid, surface->current_frame);
}
}
}
@@ -3453,6 +3499,7 @@ typedef struct DynamicPaintPaintData {
const float *avg_brushNor;
const Vec3f *brushVelocity;
+ const ParticleSystem *psys;
const float solidradius;
void *treeData;
@@ -3901,6 +3948,283 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface,
return 1;
}
+/*
+ * Paint a particle system to the surface
+ */
+static void dynamic_paint_paint_particle_cell_point_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int id, const int UNUSED(threadid))
+{
+ const DynamicPaintPaintData *data = userdata;
+
+ const DynamicPaintSurface *surface = data->surface;
+ const PaintSurfaceData *sData = surface->data;
+ const PaintBakeData *bData = sData->bData;
+ VolumeGrid *grid = bData->grid;
+
+ const DynamicPaintBrushSettings *brush = data->brush;
+
+ const ParticleSystem *psys = data->psys;
+
+ const float timescale = data->timescale;
+ const int c_index = data->c_index;
+
+ KDTree *tree = data->treeData;
+
+ const float solidradius = data->solidradius;
+ const float smooth = brush->particle_smooth * surface->radius_scale;
+ const float range = solidradius + smooth;
+ const float particle_timestep = 0.04f * psys->part->timetweak;
+
+ const int index = grid->t_index[grid->s_pos[c_index] + id];
+ float disp_intersect = 0.0f;
+ float radius = 0.0f;
+ float strength = 0.0f;
+ int part_index = -1;
+
+ /*
+ * With predefined radius, there is no variation between particles.
+ * It's enough to just find the nearest one.
+ */
+ {
+ KDTreeNearest nearest;
+ float smooth_range, part_solidradius;
+
+ /* Find nearest particle and get distance to it */
+ BLI_kdtree_find_nearest(tree, bData->realCoord[bData->s_pos[index]].v, &nearest);
+ /* if outside maximum range, no other particle can influence either */
+ if (nearest.dist > range)
+ return;
+
+ if (brush->flags & MOD_DPAINT_PART_RAD) {
+ /* use particles individual size */
+ ParticleData *pa = psys->particles + nearest.index;
+ part_solidradius = pa->size;
+ }
+ else {
+ part_solidradius = solidradius;
+ }
+ radius = part_solidradius + smooth;
+ if (nearest.dist < radius) {
+ /* distances inside solid radius has maximum influence -> dist = 0 */
+ smooth_range = max_ff(0.0f, (nearest.dist - part_solidradius));
+ /* do smoothness if enabled */
+ if (smooth)
+ smooth_range /= smooth;
+
+ strength = 1.0f - smooth_range;
+ disp_intersect = radius - nearest.dist;
+ part_index = nearest.index;
+ }
+ }
+ /* If using random per particle radius and closest particle didn't give max influence */
+ if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) {
+ /*
+ * If we use per particle radius, we have to sample all particles
+ * within max radius range
+ */
+ KDTreeNearest *nearest;
+
+ float smooth_range = smooth * (1.0f - strength), dist;
+ /* calculate max range that can have particles with higher influence than the nearest one */
+ const float max_range = smooth - strength * smooth + solidradius;
+ /* Make gcc happy! */
+ dist = max_range;
+
+ const int particles = BLI_kdtree_range_search(
+ tree, bData->realCoord[bData->s_pos[index]].v, &nearest, max_range);
+
+ /* Find particle that produces highest influence */
+ for (int n = 0; n < particles; n++) {
+ ParticleData *pa = &psys->particles[nearest[n].index];
+
+ /* skip if out of range */
+ if (nearest[n].dist > (pa->size + smooth))
+ continue;
+
+ /* update hit data */
+ const float s_range = nearest[n].dist - pa->size;
+ /* skip if higher influence is already found */
+ if (smooth_range < s_range)
+ continue;
+
+ /* update hit data */
+ smooth_range = s_range;
+ dist = nearest[n].dist;
+ part_index = nearest[n].index;
+
+ /* If inside solid range and no disp depth required, no need to seek further */
+ if ((s_range < 0.0f) && !ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) {
+ break;
+ }
+ }
+
+ if (nearest)
+ MEM_freeN(nearest);
+
+ /* now calculate influence for this particle */
+ const float rad = radius + smooth;
+ if ((rad - dist) > disp_intersect) {
+ disp_intersect = radius - dist;
+ radius = rad;
+ }
+
+ /* do smoothness if enabled */
+ CLAMP_MIN(smooth_range, 0.0f);
+ if (smooth)
+ smooth_range /= smooth;
+
+ const float str = 1.0f - smooth_range;
+ /* if influence is greater, use this one */
+ if (str > strength)
+ strength = str;
+ }
+
+ if (strength > 0.001f) {
+ float paintColor[4] = {0.0f};
+ float depth = 0.0f;
+ float velocity_val = 0.0f;
+
+ /* apply velocity */
+ if ((brush->flags & MOD_DPAINT_USES_VELOCITY) && (part_index != -1)) {
+ float velocity[3];
+ ParticleData *pa = psys->particles + part_index;
+ mul_v3_v3fl(velocity, pa->state.vel, particle_timestep);
+
+ /* substract canvas point velocity */
+ if (bData->velocity) {
+ sub_v3_v3(velocity, bData->velocity[index].v);
+ }
+ velocity_val = normalize_v3(velocity);
+
+ /* store brush velocity for smudge */
+ if ((surface->type == MOD_DPAINT_SURFACE_T_PAINT) &&
+ (brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity))
+ {
+ copy_v3_v3(&bData->brush_velocity[index * 4], velocity);
+ bData->brush_velocity[index * 4 + 3] = velocity_val;
+ }
+ }
+
+ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
+ copy_v3_v3(paintColor, &brush->r);
+ }
+ else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) {
+ /* get displace depth */
+ disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius;
+ depth = max_ff(0.0f, (radius - disp_intersect) / bData->bNormal[index].normal_scale);
+ }
+
+ dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale);
+ }
+}
+
+static int dynamicPaint_paintParticles(DynamicPaintSurface *surface,
+ ParticleSystem *psys,
+ DynamicPaintBrushSettings *brush,
+ float timescale)
+{
+ ParticleSettings *part = psys->part;
+ PaintSurfaceData *sData = surface->data;
+ PaintBakeData *bData = sData->bData;
+ VolumeGrid *grid = bData->grid;
+
+ KDTree *tree;
+ int particlesAdded = 0;
+ int invalidParticles = 0;
+ int p = 0;
+
+ const float solidradius = surface->radius_scale *
+ ((brush->flags & MOD_DPAINT_PART_RAD) ? part->size : brush->particle_radius);
+ const float smooth = brush->particle_smooth * surface->radius_scale;
+
+ const float range = solidradius + smooth;
+
+ Bounds3D part_bb = {{0}};
+
+ if (psys->totpart < 1)
+ return 1;
+
+ /*
+ * Build a kd-tree to optimize distance search
+ */
+ tree = BLI_kdtree_new(psys->totpart);
+
+ /* loop through particles and insert valid ones to the tree */
+ p = 0;
+ for (ParticleData *pa = psys->particles; p < psys->totpart; p++, pa++) {
+ /* Proceed only if particle is active */
+ if ((pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN) == 0) ||
+ (pa->alive == PARS_DEAD && (part->flag & PART_DIED) == 0) ||
+ (pa->flag & PARS_UNEXIST))
+ {
+ continue;
+ }
+
+ /* for debug purposes check if any NAN particle proceeds
+ * For some reason they get past activity check, this should rule most of them out */
+ if (isnan(pa->state.co[0]) || isnan(pa->state.co[1]) || isnan(pa->state.co[2])) {
+ invalidParticles++;
+ continue;
+ }
+
+ /* make sure particle is close enough to canvas */
+ if (!boundIntersectPoint(&grid->grid_bounds, pa->state.co, range))
+ continue;
+
+ BLI_kdtree_insert(tree, p, pa->state.co);
+
+ /* calc particle system bounds */
+ boundInsert(&part_bb, pa->state.co);
+
+ particlesAdded++;
+ }
+ if (invalidParticles)
+ printf("Warning: Invalid particle(s) found!\n");
+
+ /* If no suitable particles were found, exit */
+ if (particlesAdded < 1) {
+ BLI_kdtree_free(tree);
+ return 1;
+ }
+
+ /* begin thread safe malloc */
+ BLI_begin_threaded_malloc();
+
+ /* only continue if particle bb is close enough to canvas bb */
+ if (boundsIntersectDist(&grid->grid_bounds, &part_bb, range)) {
+ int c_index;
+ int total_cells = grid->dim[0] * grid->dim[1] * grid->dim[2];
+
+ /* balance tree */
+ BLI_kdtree_balance(tree);
+
+ /* loop through space partitioning grid */
+ for (c_index = 0; c_index < total_cells; c_index++) {
+ /* check cell bounding box */
+ if (!grid->s_num[c_index] ||
+ !boundsIntersectDist(&grid->bounds[c_index], &part_bb, range))
+ {
+ continue;
+ }
+
+ /* loop through cell points */
+ DynamicPaintPaintData data = {
+ .surface = surface,
+ .brush = brush, .psys = psys,
+ .solidradius = solidradius, .timescale = timescale, .c_index = c_index,
+ .treeData = tree,
+ };
+ BLI_task_parallel_range_ex(0, grid->s_num[c_index], &data, NULL, 0,
+ dynamic_paint_paint_particle_cell_point_cb_ex,
+ grid->s_num[c_index] > 250, true);
+ }
+ }
+ BLI_end_threaded_malloc();
+ BLI_kdtree_free(tree);
+
+ return 1;
+}
+
/* paint a single point of defined proximity radius to the surface */
static void dynamic_paint_paint_single_point_cb_ex(
void *userdata, void *UNUSED(userdata_chunk), const int index, const int UNUSED(threadid))
@@ -4334,7 +4658,7 @@ static int dynamicPaint_prepareEffectStep(
/* Init force data if required */
if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
- ListBase *effectors = pdInitEffectors(scene, ob, surface->effector_weights, true);
+ ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true);
/* allocate memory for force data (dir vector + strength) */
*force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces");
@@ -5238,6 +5562,15 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su
dynamicPaint_updateBrushMaterials(brushObj, brush->mat, scene, &bMats);
/* Apply brush on the surface depending on it's collision type */
+ if (brush->psys && brush->psys->part &&
+ ELEM(brush->psys->part->type, PART_EMITTER, PART_FLUID) &&
+ psys_check_enabled(brushObj, brush->psys, G.is_rendering))
+ {
+ /* Paint a particle system */
+ BKE_animsys_evaluate_animdata(scene, &brush->psys->part->id, brush->psys->part->adt,
+ BKE_scene_frame_get(scene), ADT_RECALC_ANIM);
+ dynamicPaint_paintParticles(surface, brush->psys, brush, timescale);
+ }
/* Object center distance: */
if (brush->collision == MOD_DPAINT_COL_POINT && brushObj != ob) {
dynamicPaint_paintSinglePoint(surface, brushObj->loc, brush, brushObj, &bMats, scene, timescale);
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 33125ceeb02..fe8f5ebdca6 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -43,6 +43,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
#include "DNA_texture_types.h"
#include "DNA_scene_types.h"
@@ -66,6 +67,7 @@
#include "BKE_library.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_smoke.h"
@@ -143,11 +145,12 @@ void free_partdeflect(PartDeflect *pd)
MEM_freeN(pd);
}
-static EffectorCache *new_effector_cache(Scene *scene, Object *ob, PartDeflect *pd)
+static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSystem *psys, PartDeflect *pd)
{
EffectorCache *eff = MEM_callocN(sizeof(EffectorCache), "EffectorCache");
eff->scene = scene;
eff->ob = ob;
+ eff->psys = psys;
eff->pd = pd;
eff->frame = -1;
return eff;
@@ -170,16 +173,40 @@ static void add_object_to_effectors(ListBase **effectors, Scene *scene, Effector
if (*effectors == NULL)
*effectors = MEM_callocN(sizeof(ListBase), "effectors list");
- eff = new_effector_cache(scene, ob, ob->pd);
+ eff = new_effector_cache(scene, ob, NULL, ob->pd);
/* make sure imat is up to date */
invert_m4_m4(ob->imat, ob->obmat);
BLI_addtail(*effectors, eff);
}
+static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src, bool for_simulation)
+{
+ ParticleSettings *part= psys->part;
+
+ if ( !psys_check_enabled(ob, psys, G.is_rendering) )
+ return;
+
+ if ( psys == psys_src && (part->flag & PART_SELF_EFFECT) == 0)
+ return;
+
+ if ( part->pd && part->pd->forcefield && (!for_simulation || weights->weight[part->pd->forcefield] != 0.0f)) {
+ if (*effectors == NULL)
+ *effectors = MEM_callocN(sizeof(ListBase), "effectors list");
+
+ BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd));
+ }
+
+ if (part->pd2 && part->pd2->forcefield && (!for_simulation || weights->weight[part->pd2->forcefield] != 0.0f)) {
+ if (*effectors == NULL)
+ *effectors = MEM_callocN(sizeof(ListBase), "effectors list");
+
+ BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd2));
+ }
+}
/* returns ListBase handle with objects taking part in the effecting */
-ListBase *pdInitEffectors(Scene *scene, Object *ob_src,
+ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src,
EffectorWeights *weights, bool for_simulation)
{
Base *base;
@@ -193,6 +220,13 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src,
if ( (go->ob->lay & layer) ) {
if ( go->ob->pd && go->ob->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src, for_simulation);
+
+ if ( go->ob->particlesystem.first ) {
+ ParticleSystem *psys= go->ob->particlesystem.first;
+
+ for ( ; psys; psys=psys->next )
+ add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src, for_simulation);
+ }
}
}
}
@@ -201,6 +235,13 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src,
if ( (base->lay & layer) ) {
if ( base->object->pd && base->object->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, base->object, ob_src, for_simulation);
+
+ if ( base->object->particlesystem.first ) {
+ ParticleSystem *psys= base->object->particlesystem.first;
+
+ for ( ; psys; psys=psys->next )
+ add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src, for_simulation);
+ }
}
}
}
@@ -253,6 +294,8 @@ static void precalculate_effector(EffectorCache *eff)
if (eff->ob->type == OB_CURVE)
eff->flag |= PE_USE_NORMAL_DATA;
}
+ else if (eff->psys)
+ psys_update_particle_tree(eff->psys, eff->scene->r.cfra);
/* Store object velocity */
if (eff->ob) {
@@ -275,6 +318,36 @@ void pdPrecalculateEffectors(ListBase *effectors)
}
+void pd_point_from_particle(ParticleSimulationData *sim, ParticleData *pa, ParticleKey *state, EffectedPoint *point)
+{
+ ParticleSettings *part = sim->psys->part;
+ point->loc = state->co;
+ point->vel = state->vel;
+ point->index = pa - sim->psys->particles;
+ point->size = pa->size;
+ point->charge = 0.0f;
+
+ if (part->pd && part->pd->forcefield == PFIELD_CHARGE)
+ point->charge += part->pd->f_strength;
+
+ if (part->pd2 && part->pd2->forcefield == PFIELD_CHARGE)
+ point->charge += part->pd2->f_strength;
+
+ point->vel_to_sec = 1.0f;
+ point->vel_to_frame = psys_get_timestep(sim);
+
+ point->flag = 0;
+
+ if (sim->psys->part->flag & PART_ROT_DYN) {
+ point->ave = state->ave;
+ point->rot = state->rot;
+ }
+ else
+ point->ave = point->rot = NULL;
+
+ point->psys = sim->psys;
+}
+
void pd_point_from_loc(Scene *scene, float *loc, float *vel, int index, EffectedPoint *point)
{
point->loc = loc;
@@ -288,6 +361,7 @@ void pd_point_from_loc(Scene *scene, float *loc, float *vel, int index, Effected
point->flag = 0;
point->ave = point->rot = NULL;
+ point->psys = NULL;
}
void pd_point_from_soft(Scene *scene, float *loc, float *vel, int index, EffectedPoint *point)
{
@@ -302,6 +376,8 @@ void pd_point_from_soft(Scene *scene, float *loc, float *vel, int index, Effecte
point->flag = PE_WIND_AS_SPEED;
point->ave = point->rot = NULL;
+
+ point->psys = NULL;
}
/************************************************/
/* Effectors */
@@ -414,7 +490,7 @@ static float falloff_func_rad(PartDeflect *pd, float fac)
return falloff_func(fac, pd->flag&PFIELD_USEMINR, pd->minrad, pd->flag&PFIELD_USEMAXR, pd->maxrad, pd->f_power_r);
}
-static float effector_falloff(EffectorCache *eff, EffectorData *efd, EffectedPoint *UNUSED(point), EffectorWeights *weights)
+float effector_falloff(EffectorCache *eff, EffectorData *efd, EffectedPoint *UNUSED(point), EffectorWeights *weights)
{
float temp[3];
float falloff = weights ? weights->weight[0] * weights->weight[eff->pd->forcefield] : 1.0f;
@@ -489,6 +565,7 @@ int closest_point_on_surface(SurfaceModifierData *surmd, const float co[3], floa
}
int get_effector_data(EffectorCache *eff, EffectorData *efd, EffectedPoint *point, int real_velocity)
{
+ float cfra = eff->scene->r.cfra;
int ret = 0;
/* In case surface object is in Edit mode when loading the .blend, surface modifier is never executed
@@ -525,6 +602,43 @@ int get_effector_data(EffectorCache *eff, EffectorData *efd, EffectedPoint *poin
ret = 1;
}
}
+ else if (eff->psys) {
+ ParticleData *pa = eff->psys->particles + *efd->index;
+ ParticleKey state;
+
+ /* exclude the particle itself for self effecting particles */
+ if (eff->psys == point->psys && *efd->index == point->index) {
+ /* pass */
+ }
+ else {
+ ParticleSimulationData sim= {NULL};
+ sim.scene= eff->scene;
+ sim.ob= eff->ob;
+ sim.psys= eff->psys;
+
+ /* TODO: time from actual previous calculated frame (step might not be 1) */
+ state.time = cfra - 1.0f;
+ ret = psys_get_particle_state(&sim, *efd->index, &state, 0);
+
+ /* TODO */
+ //if (eff->pd->forcefiled == PFIELD_HARMONIC && ret==0) {
+ // if (pa->dietime < eff->psys->cfra)
+ // eff->flag |= PE_VELOCITY_TO_IMPULSE;
+ //}
+
+ copy_v3_v3(efd->loc, state.co);
+
+ /* rather than use the velocity use rotated x-axis (defaults to velocity) */
+ efd->nor[0] = 1.f;
+ efd->nor[1] = efd->nor[2] = 0.f;
+ mul_qt_v3(state.rot, efd->nor);
+
+ if (real_velocity)
+ copy_v3_v3(efd->vel, state.vel);
+
+ efd->size = pa->size;
+ }
+ }
else {
/* use center of object for distance calculus */
const Object *ob = eff->ob;
@@ -576,7 +690,7 @@ int get_effector_data(EffectorCache *eff, EffectorData *efd, EffectedPoint *poin
return ret;
}
-static void get_effector_tot(EffectorCache *eff, EffectorData *efd, EffectedPoint *point, int *tot, int *p)
+static void get_effector_tot(EffectorCache *eff, EffectorData *efd, EffectedPoint *point, int *tot, int *p, int *step)
{
*p = 0;
efd->index = p;
@@ -589,6 +703,31 @@ static void get_effector_tot(EffectorCache *eff, EffectorData *efd, EffectedPoin
*tot = *p+1;
}
}
+ else if (eff->psys) {
+ *tot = eff->psys->totpart;
+
+ if (eff->pd->forcefield == PFIELD_CHARGE) {
+ /* Only the charge of the effected particle is used for
+ * interaction, not fall-offs. If the fall-offs aren't the
+ * same this will be unphysical, but for animation this
+ * could be the wanted behavior. If you want physical
+ * correctness the fall-off should be spherical 2.0 anyways.
+ */
+ efd->charge = eff->pd->f_strength;
+ }
+ else if (eff->pd->forcefield == PFIELD_HARMONIC && (eff->pd->flag & PFIELD_MULTIPLE_SPRINGS)==0) {
+ /* every particle is mapped to only one harmonic effector particle */
+ *p= point->index % eff->psys->totpart;
+ *tot= *p + 1;
+ }
+
+ if (eff->psys->part->effector_amount) {
+ int totpart = eff->psys->totpart;
+ int amount = eff->psys->part->effector_amount;
+
+ *step = (totpart > amount) ? totpart/amount : 1;
+ }
+ }
else {
*tot = 1;
}
@@ -854,7 +993,7 @@ void pdDoEffectors(ListBase *effectors, ListBase *colliders, EffectorWeights *we
*/
EffectorCache *eff;
EffectorData efd;
- int p=0, tot = 1;
+ int p=0, tot = 1, step = 1;
/* Cycle through collected objects, get total of (1/(gravity_strength * dist^gravity_power)) */
/* Check for min distance here? (yes would be cool to add that, ton) */
@@ -862,9 +1001,9 @@ void pdDoEffectors(ListBase *effectors, ListBase *colliders, EffectorWeights *we
if (effectors) for (eff = effectors->first; eff; eff=eff->next) {
/* object effectors were fully checked to be OK to evaluate! */
- get_effector_tot(eff, &efd, point, &tot, &p);
+ get_effector_tot(eff, &efd, point, &tot, &p, &step);
- for (; p<tot; p++) {
+ for (; p<tot; p+=step) {
if (get_effector_data(eff, &efd, point, 0)) {
efd.falloff= effector_falloff(eff, &efd, point, weights);
diff --git a/source/blender/blenkernel/intern/fluidsim.c b/source/blender/blenkernel/intern/fluidsim.c
index 8874e059d6c..8247336d915 100644
--- a/source/blender/blenkernel/intern/fluidsim.c
+++ b/source/blender/blenkernel/intern/fluidsim.c
@@ -43,6 +43,7 @@
#include "DNA_object_fluidsim.h"
#include "DNA_object_force.h" // for pointcache
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "BLI_math.h"
diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c
index 708472fa4a5..9b011dbb003 100644
--- a/source/blender/blenkernel/intern/group.c
+++ b/source/blender/blenkernel/intern/group.c
@@ -40,6 +40,7 @@
#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_particle_types.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c
index c76d072cb64..70d037d85f3 100644
--- a/source/blender/blenkernel/intern/idcode.c
+++ b/source/blender/blenkernel/intern/idcode.c
@@ -78,6 +78,7 @@ static IDType idtypes[] = {
{ ID_MSK, "Mask", "masks", BLT_I18NCONTEXT_ID_MASK, IDTYPE_FLAGS_ISLINKABLE },
{ ID_NT, "NodeTree", "node_groups", BLT_I18NCONTEXT_ID_NODETREE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_OB, "Object", "objects", BLT_I18NCONTEXT_ID_OBJECT, IDTYPE_FLAGS_ISLINKABLE },
+ { ID_PA, "ParticleSettings", "particles", BLT_I18NCONTEXT_ID_PARTICLESETTINGS, IDTYPE_FLAGS_ISLINKABLE },
{ ID_PAL, "Palettes", "palettes", BLT_I18NCONTEXT_ID_PALETTE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_PC, "PaintCurve", "paint_curves", BLT_I18NCONTEXT_ID_PAINTCURVE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_SCE, "Scene", "scenes", BLT_I18NCONTEXT_ID_SCENE, IDTYPE_FLAGS_ISLINKABLE },
@@ -199,6 +200,7 @@ int BKE_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(MSK);
CASE_IDFILTER(NT);
CASE_IDFILTER(OB);
+ CASE_IDFILTER(PA);
CASE_IDFILTER(PAL);
CASE_IDFILTER(PC);
CASE_IDFILTER(SCE);
@@ -242,6 +244,7 @@ short BKE_idcode_from_idfilter(const int idfilter)
CASE_IDFILTER(MSK);
CASE_IDFILTER(NT);
CASE_IDFILTER(OB);
+ CASE_IDFILTER(PA);
CASE_IDFILTER(PAL);
CASE_IDFILTER(PC);
CASE_IDFILTER(SCE);
@@ -288,6 +291,7 @@ int BKE_idcode_to_index(const short idcode)
CASE_IDINDEX(MSK);
CASE_IDINDEX(NT);
CASE_IDINDEX(OB);
+ CASE_IDINDEX(PA);
CASE_IDINDEX(PAL);
CASE_IDINDEX(PC);
CASE_IDINDEX(SCE);
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index b7eb80cbe2a..730d5a93758 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -739,6 +739,74 @@ static const char *world_adrcodes_to_paths(int adrcode, int *array_index)
return NULL;
}
+/* Particle Types */
+static const char *particle_adrcodes_to_paths(int adrcode, int *array_index)
+{
+ /* set array index like this in-case nothing sets it correctly */
+ *array_index = 0;
+
+ /* result depends on adrcode */
+ switch (adrcode) {
+ case PART_CLUMP:
+ return "settings.clump_factor";
+ case PART_AVE:
+ return "settings.angular_velocity_factor";
+ case PART_SIZE:
+ return "settings.particle_size";
+ case PART_DRAG:
+ return "settings.drag_factor";
+ case PART_BROWN:
+ return "settings.brownian_factor";
+ case PART_DAMP:
+ return "settings.damp_factor";
+ case PART_LENGTH:
+ return "settings.length";
+ case PART_GRAV_X:
+ *array_index = 0; return "settings.acceleration";
+ case PART_GRAV_Y:
+ *array_index = 1; return "settings.acceleration";
+ case PART_GRAV_Z:
+ *array_index = 2; return "settings.acceleration";
+ case PART_KINK_AMP:
+ return "settings.kink_amplitude";
+ case PART_KINK_FREQ:
+ return "settings.kink_frequency";
+ case PART_KINK_SHAPE:
+ return "settings.kink_shape";
+ case PART_BB_TILT:
+ return "settings.billboard_tilt";
+
+ /* PartDeflect needs to be sorted out properly in rna_object_force;
+ * If anyone else works on this, but is unfamiliar, these particular
+ * settings reference the particles of the system themselves
+ * being used as forces -- it will use the same rna structure
+ * as the similar object forces */
+#if 0
+ case PART_PD_FSTR:
+ if (part->pd) poin = &(part->pd->f_strength);
+ break;
+ case PART_PD_FFALL:
+ if (part->pd) poin = &(part->pd->f_power);
+ break;
+ case PART_PD_FMAXD:
+ if (part->pd) poin = &(part->pd->maxdist);
+ break;
+ case PART_PD2_FSTR:
+ if (part->pd2) poin = &(part->pd2->f_strength);
+ break;
+ case PART_PD2_FFALL:
+ if (part->pd2) poin = &(part->pd2->f_power);
+ break;
+ case PART_PD2_FMAXD:
+ if (part->pd2) poin = &(part->pd2->maxdist);
+ break;
+#endif
+
+ }
+
+ return NULL;
+}
+
/* ------- */
/* Allocate memory for RNA-path for some property given a blocktype, adrcode, and 'root' parts of path
@@ -804,6 +872,10 @@ static char *get_rna_access(ID *id, int blocktype, int adrcode, char actname[],
propname = world_adrcodes_to_paths(adrcode, &dummy_index);
break;
+ case ID_PA: /* particle */
+ propname = particle_adrcodes_to_paths(adrcode, &dummy_index);
+ break;
+
case ID_CU: /* curve */
/* this used to be a 'dummy' curve which got evaluated on the fly...
* now we've got real var for this!
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 4b854289814..038504040d7 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -113,6 +113,7 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
#include "BKE_packedFile.h"
#include "BKE_sound.h"
#include "BKE_speaker.h"
@@ -435,6 +436,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
case ID_BR:
if (!test) BKE_brush_make_local(bmain, (Brush *)id, lib_local);
return true;
+ case ID_PA:
+ if (!test) BKE_particlesettings_make_local(bmain, (ParticleSettings *)id, lib_local);
+ return true;
case ID_GD:
if (!test) BKE_gpencil_make_local(bmain, (bGPdata *)id, lib_local);
return true;
@@ -539,6 +543,9 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
case ID_BR:
if (!test) *newid = (ID *)BKE_brush_copy(bmain, (Brush *)id);
return true;
+ case ID_PA:
+ if (!test) *newid = (ID *)BKE_particlesettings_copy(bmain, (ParticleSettings *)id);
+ return true;
case ID_GD:
if (!test) *newid = (ID *)BKE_gpencil_data_duplicate(bmain, (bGPdata *)id, false);
return true;
@@ -657,6 +664,8 @@ ListBase *which_libbase(Main *mainlib, short type)
return &(mainlib->nodetree);
case ID_BR:
return &(mainlib->brush);
+ case ID_PA:
+ return &(mainlib->particle);
case ID_WM:
return &(mainlib->wm);
case ID_GD:
@@ -809,6 +818,7 @@ int set_listbasepointers(Main *main, ListBase **lb)
lb[INDEX_ID_PAL] = &(main->palettes);
lb[INDEX_ID_PC] = &(main->paintcurves);
lb[INDEX_ID_BR] = &(main->brush);
+ lb[INDEX_ID_PA] = &(main->particle);
lb[INDEX_ID_SPK] = &(main->speaker);
lb[INDEX_ID_WO] = &(main->world);
@@ -919,6 +929,9 @@ void *BKE_libblock_alloc_notest(short type)
case ID_BR:
id = MEM_callocN(sizeof(Brush), "brush");
break;
+ case ID_PA:
+ id = MEM_callocN(sizeof(ParticleSettings), "ParticleSettings");
+ break;
case ID_WM:
id = MEM_callocN(sizeof(wmWindowManager), "Window manager");
break;
@@ -1054,6 +1067,9 @@ void BKE_libblock_init_empty(ID *id)
case ID_BR:
BKE_brush_init((Brush *)id);
break;
+ case ID_PA:
+ /* Nothing to do. */
+ break;
case ID_PC:
/* Nothing to do. */
break;
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c
index a161d9c0879..bfc26fcac0f 100644
--- a/source/blender/blenkernel/intern/library_query.c
+++ b/source/blender/blenkernel/intern/library_query.c
@@ -51,7 +51,6 @@
#include "DNA_mask_types.h"
#include "DNA_node_types.h"
#include "DNA_object_force.h"
-#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_sensor_types.h"
@@ -74,6 +73,7 @@
#include "BKE_library_query.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
+#include "BKE_particle.h"
#include "BKE_rigidbody.h"
#include "BKE_sca.h"
#include "BKE_sequencer.h"
@@ -166,6 +166,15 @@ static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), ID
FOREACH_FINALIZE_VOID;
}
+static void library_foreach_particlesystemsObjectLooper(
+ ParticleSystem *UNUSED(psys), ID **id_pointer, void *user_data, int cd_flag)
+{
+ LibraryForeachIDData *data = (LibraryForeachIDData *) user_data;
+ FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag);
+
+ FOREACH_FINALIZE_VOID;
+}
+
static void library_foreach_sensorsObjectLooper(
bSensor *UNUSED(sensor), ID **id_pointer, void *user_data, int cd_flag)
{
@@ -392,6 +401,9 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
if (toolsett) {
CALLBACK_INVOKE(toolsett->skgen_template, IDWALK_NOP);
+ CALLBACK_INVOKE(toolsett->particle.scene, IDWALK_NOP);
+ CALLBACK_INVOKE(toolsett->particle.object, IDWALK_NOP);
+ CALLBACK_INVOKE(toolsett->particle.shape_object, IDWALK_NOP);
library_foreach_paint(&data, &toolsett->imapaint.paint);
CALLBACK_INVOKE(toolsett->imapaint.stencil, IDWALK_USER);
CALLBACK_INVOKE(toolsett->imapaint.clone, IDWALK_USER);
@@ -424,6 +436,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
case ID_OB:
{
Object *object = (Object *) id;
+ ParticleSystem *psys;
/* Object is special, proxies make things hard... */
const int data_cd_flag = data.cd_flag;
@@ -501,6 +514,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
modifiers_foreachIDLink(object, library_foreach_modifiersForeachIDLink, &data);
BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, &data);
+ for (psys = object->particlesystem.first; psys; psys = psys->next) {
+ BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, &data);
+ }
+
if (object->soft) {
CALLBACK_INVOKE(object->soft->collision_group, IDWALK_NOP);
@@ -715,6 +732,53 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
break;
}
+ case ID_PA:
+ {
+ ParticleSettings *psett = (ParticleSettings *) id;
+ CALLBACK_INVOKE(psett->dup_group, IDWALK_NOP);
+ CALLBACK_INVOKE(psett->dup_ob, IDWALK_NOP);
+ CALLBACK_INVOKE(psett->bb_ob, IDWALK_NOP);
+ CALLBACK_INVOKE(psett->collision_group, IDWALK_NOP);
+
+ for (i = 0; i < MAX_MTEX; i++) {
+ if (psett->mtex[i]) {
+ library_foreach_mtex(&data, psett->mtex[i]);
+ }
+ }
+
+ if (psett->effector_weights) {
+ CALLBACK_INVOKE(psett->effector_weights->group, IDWALK_NOP);
+ }
+
+ if (psett->pd) {
+ CALLBACK_INVOKE(psett->pd->tex, IDWALK_USER);
+ CALLBACK_INVOKE(psett->pd->f_source, IDWALK_NOP);
+ }
+ if (psett->pd2) {
+ CALLBACK_INVOKE(psett->pd2->tex, IDWALK_USER);
+ CALLBACK_INVOKE(psett->pd2->f_source, IDWALK_NOP);
+ }
+
+ if (psett->boids) {
+ BoidState *state;
+ BoidRule *rule;
+
+ for (state = psett->boids->states.first; state; state = state->next) {
+ for (rule = state->rules.first; rule; rule = rule->next) {
+ if (rule->type == eBoidRuleType_Avoid) {
+ BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
+ CALLBACK_INVOKE(gabr->ob, IDWALK_NOP);
+ }
+ else if (rule->type == eBoidRuleType_FollowLeader) {
+ BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule;
+ CALLBACK_INVOKE(flbr->ob, IDWALK_NOP);
+ }
+ }
+ }
+ }
+ break;
+ }
+
case ID_MC:
{
MovieClip *clip = (MovieClip *) id;
@@ -919,6 +983,8 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
#endif
case ID_BR:
return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE);
+ case ID_PA:
+ return ELEM(id_type_used, ID_OB, ID_GR, ID_TE);
case ID_MC:
return ELEM(id_type_used, ID_GD, ID_IM);
case ID_MSK:
diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c
index a257621dc2d..4f1f6d963ed 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -98,6 +98,7 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
#include "BKE_sca.h"
#include "BKE_speaker.h"
#include "BKE_sound.h"
@@ -816,6 +817,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const b
case ID_BR:
BKE_brush_free((Brush *)id);
break;
+ case ID_PA:
+ BKE_particlesettings_free((ParticleSettings *)id);
+ break;
case ID_WM:
if (free_windowmanager_cb)
free_windowmanager_cb(NULL, (wmWindowManager *)id);
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 936b014cca3..41e4c21d814 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -401,6 +401,13 @@ bool modifiers_isModifierEnabled(Object *ob, int modifierType)
return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render));
}
+bool modifiers_isParticleEnabled(Object *ob)
+{
+ ModifierData *md = modifiers_findByType(ob, eModifierType_ParticleSystem);
+
+ return (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render));
+}
+
bool modifier_isEnabled(struct Scene *scene, ModifierData *md, int required_mode)
{
const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 432adfaef53..c6666ec5ced 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -50,8 +50,6 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_movieclip_types.h"
-#include "DNA_object_force.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
@@ -106,6 +104,8 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_property.h"
#include "BKE_rigidbody.h"
#include "BKE_sca.h"
@@ -161,6 +161,15 @@ void BKE_object_update_base_layer(struct Scene *scene, Object *ob)
}
}
+void BKE_object_free_particlesystems(Object *ob)
+{
+ ParticleSystem *psys;
+
+ while ((psys = BLI_pophead(&ob->particlesystem))) {
+ psys_free(ob, psys);
+ }
+}
+
void BKE_object_free_softbody(Object *ob)
{
if (ob->soft) {
@@ -199,6 +208,9 @@ void BKE_object_free_modifiers(Object *ob)
modifier_free(md);
}
+ /* particle modifiers were freed, so free the particlesystems as well */
+ BKE_object_free_particlesystems(ob);
+
/* same for softbody */
BKE_object_free_softbody(ob);
@@ -295,6 +307,8 @@ void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_sr
modifier_unique_name(&ob_dst->modifiers, nmd);
}
+ BKE_object_copy_particlesystems(ob_dst, ob_src);
+
/* TODO: smoke?, cloth? */
}
@@ -338,8 +352,40 @@ void BKE_object_free_derived_caches(Object *ob)
void BKE_object_free_caches(Object *object)
{
+ ModifierData *md;
short update_flag = 0;
+ /* Free particle system caches holding paths. */
+ if (object->particlesystem.first) {
+ ParticleSystem *psys;
+ for (psys = object->particlesystem.first;
+ psys != NULL;
+ psys = psys->next)
+ {
+ psys_free_path_cache(psys, psys->edit);
+ update_flag |= PSYS_RECALC_REDO;
+ }
+ }
+
+ /* Free memory used by cached derived meshes in the particle system modifiers. */
+ for (md = object->modifiers.first; md != NULL; md = md->next) {
+ if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md;
+ if (psmd->dm_final != NULL) {
+ psmd->dm_final->needsFree = 1;
+ psmd->dm_final->release(psmd->dm_final);
+ psmd->dm_final = NULL;
+ if (psmd->dm_deformed != NULL) {
+ psmd->dm_deformed->needsFree = 1;
+ psmd->dm_deformed->release(psmd->dm_deformed);
+ psmd->dm_deformed = NULL;
+ }
+ psmd->flag |= eParticleSystemFlag_file_loaded;
+ update_flag |= OB_RECALC_DATA;
+ }
+ }
+ }
+
/* Tag object for update, so once memory critical operation is over and
* scene update routines are back to it's business the object will be
* guaranteed to be in a known state.
@@ -818,6 +864,8 @@ SoftBody *copy_softbody(const SoftBody *sb, bool copy_caches)
sbn->scratch = NULL;
+ sbn->pointcache = BKE_ptcache_copy_list(&sbn->ptcaches, &sb->ptcaches, copy_caches);
+
if (sb->effector_weights)
sbn->effector_weights = MEM_dupallocN(sb->effector_weights);
@@ -835,6 +883,119 @@ BulletSoftBody *copy_bulletsoftbody(BulletSoftBody *bsb)
return bsbn;
}
+ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys)
+{
+ ParticleSystem *psysn;
+ ParticleData *pa;
+ int p;
+
+ psysn = MEM_dupallocN(psys);
+ psysn->particles = MEM_dupallocN(psys->particles);
+ psysn->child = MEM_dupallocN(psys->child);
+
+ if (psys->part->type == PART_HAIR) {
+ for (p = 0, pa = psysn->particles; p < psysn->totpart; p++, pa++)
+ pa->hair = MEM_dupallocN(pa->hair);
+ }
+
+ if (psysn->particles && (psysn->particles->keys || psysn->particles->boid)) {
+ ParticleKey *key = psysn->particles->keys;
+ BoidParticle *boid = psysn->particles->boid;
+
+ if (key)
+ key = MEM_dupallocN(key);
+
+ if (boid)
+ boid = MEM_dupallocN(boid);
+
+ for (p = 0, pa = psysn->particles; p < psysn->totpart; p++, pa++) {
+ if (boid)
+ pa->boid = boid++;
+ if (key) {
+ pa->keys = key;
+ key += pa->totkey;
+ }
+ }
+ }
+
+ if (psys->clmd) {
+ psysn->clmd = (ClothModifierData *)modifier_new(eModifierType_Cloth);
+ modifier_copyData((ModifierData *)psys->clmd, (ModifierData *)psysn->clmd);
+ psys->hair_in_dm = psys->hair_out_dm = NULL;
+ }
+
+ BLI_duplicatelist(&psysn->targets, &psys->targets);
+
+ psysn->pathcache = NULL;
+ psysn->childcache = NULL;
+ psysn->edit = NULL;
+ psysn->pdd = NULL;
+ psysn->effectors = NULL;
+ psysn->tree = NULL;
+ psysn->bvhtree = NULL;
+
+ BLI_listbase_clear(&psysn->pathcachebufs);
+ BLI_listbase_clear(&psysn->childcachebufs);
+ psysn->renderdata = NULL;
+
+ psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, false);
+
+ /* XXX - from reading existing code this seems correct but intended usage of
+ * pointcache should /w cloth should be added in 'ParticleSystem' - campbell */
+ if (psysn->clmd) {
+ psysn->clmd->point_cache = psysn->pointcache;
+ }
+
+ id_us_plus((ID *)psysn->part);
+
+ return psysn;
+}
+
+void BKE_object_copy_particlesystems(Object *ob_dst, const Object *ob_src)
+{
+ ParticleSystem *psys, *npsys;
+ ModifierData *md;
+
+ if (ob_dst->type != OB_MESH) {
+ /* currently only mesh objects can have soft body */
+ return;
+ }
+
+ BLI_listbase_clear(&ob_dst->particlesystem);
+ for (psys = ob_src->particlesystem.first; psys; psys = psys->next) {
+ npsys = BKE_object_copy_particlesystem(psys);
+
+ BLI_addtail(&ob_dst->particlesystem, npsys);
+
+ /* need to update particle modifiers too */
+ for (md = ob_dst->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
+ if (psmd->psys == psys)
+ psmd->psys = npsys;
+ }
+ else if (md->type == eModifierType_DynamicPaint) {
+ DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
+ if (pmd->brush) {
+ if (pmd->brush->psys == psys) {
+ pmd->brush->psys = npsys;
+ }
+ }
+ }
+ else if (md->type == eModifierType_Smoke) {
+ SmokeModifierData *smd = (SmokeModifierData *) md;
+
+ if (smd->type == MOD_SMOKE_TYPE_FLOW) {
+ if (smd->flow) {
+ if (smd->flow->psys == psys)
+ smd->flow->psys = npsys;
+ }
+ }
+ }
+ }
+ }
+}
+
void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src)
{
if (ob_src->soft) {
@@ -991,6 +1152,8 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
obn->rigidbody_object = BKE_rigidbody_copy_object(ob);
obn->rigidbody_constraint = BKE_rigidbody_copy_constraint(ob);
+ BKE_object_copy_particlesystems(obn, ob);
+
obn->derivedDeform = NULL;
obn->derivedFinal = NULL;
@@ -3515,6 +3678,25 @@ bool BKE_object_modifier_use_time(Object *ob, ModifierData *md)
return false;
}
+/* set "ignore cache" flag for all caches on this object */
+static void object_cacheIgnoreClear(Object *ob, int state)
+{
+ ListBase pidlist;
+ PTCacheID *pid;
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache) {
+ if (state)
+ pid->cache->flag |= PTCACHE_IGNORE_CLEAR;
+ else
+ pid->cache->flag &= ~PTCACHE_IGNORE_CLEAR;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+}
+
/* Note: this function should eventually be replaced by depsgraph functionality.
* Avoid calling this in new code unless there is a very good reason for it!
*/
@@ -3574,7 +3756,11 @@ bool BKE_object_modifier_update_subframe(Scene *scene, Object *ob, bool update_m
ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, frame, ADT_RECALC_ANIM);
if (update_mesh) {
+ /* ignore cache clear during subframe updates
+ * to not mess up cache validity */
+ object_cacheIgnoreClear(ob, 1);
BKE_object_handle_update(G.main->eval_ctx, scene, ob);
+ object_cacheIgnoreClear(ob, 0);
}
else
BKE_object_where_is_calc_time(scene, ob, frame);
diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c
index 91c67899dfb..b5e1ded35bb 100644
--- a/source/blender/blenkernel/intern/object_deform.c
+++ b/source/blender/blenkernel/intern/object_deform.c
@@ -42,6 +42,7 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "BKE_action.h"
@@ -71,6 +72,8 @@ static Lattice *object_defgroup_lattice_get(ID *id)
void BKE_object_defgroup_remap_update_users(Object *ob, int *map)
{
ModifierData *md;
+ ParticleSystem *psys;
+ int a;
/* these cases don't use names to refer to vertex groups, so when
* they get removed the numbers get out of sync, this corrects that */
@@ -95,6 +98,12 @@ void BKE_object_defgroup_remap_update_users(Object *ob, int *map)
}
}
}
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ for (a = 0; a < PSYS_TOT_VG; a++) {
+ psys->vgroup[a] = map[psys->vgroup[a]];
+ }
+ }
}
/** \} */
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index ef3b1559d5d..e3b801b3193 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -44,7 +44,6 @@
#include "DNA_anim_types.h"
#include "DNA_group_types.h"
#include "DNA_mesh_types.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
@@ -58,6 +57,7 @@
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_editmesh.h"
#include "BKE_anim.h"
@@ -824,6 +824,327 @@ const DupliGenerator gen_dupli_faces = {
make_duplis_faces /* make_duplis */
};
+/* OB_DUPLIPARTS */
+static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem *psys)
+{
+ Scene *scene = ctx->scene;
+ Object *par = ctx->object;
+ bool for_render = ctx->eval_ctx->mode == DAG_EVAL_RENDER;
+ bool use_texcoords = ELEM(ctx->eval_ctx->mode, DAG_EVAL_RENDER, DAG_EVAL_PREVIEW);
+
+ GroupObject *go;
+ Object *ob = NULL, **oblist = NULL, obcopy, *obcopylist = NULL;
+ DupliObject *dob;
+ ParticleDupliWeight *dw;
+ ParticleSettings *part;
+ ParticleData *pa;
+ ChildParticle *cpa = NULL;
+ ParticleKey state;
+ ParticleCacheKey *cache;
+ float ctime, pa_time, scale = 1.0f;
+ float tmat[4][4], mat[4][4], pamat[4][4], vec[3], size = 0.0;
+ float (*obmat)[4];
+ int a, b, hair = 0;
+ int totpart, totchild, totgroup = 0 /*, pa_num */;
+ const bool dupli_type_hack = !BKE_scene_use_new_shading_nodes(scene);
+
+ int no_draw_flag = PARS_UNEXIST;
+
+ if (psys == NULL) return;
+
+ part = psys->part;
+
+ if (part == NULL)
+ return;
+
+ if (!psys_check_enabled(par, psys, (ctx->eval_ctx->mode == DAG_EVAL_RENDER)))
+ return;
+
+ if (!for_render)
+ no_draw_flag |= PARS_NO_DISP;
+
+ ctime = BKE_scene_frame_get(scene); /* NOTE: in old animsys, used parent object's timeoffset... */
+
+ totpart = psys->totpart;
+ totchild = psys->totchild;
+
+ BLI_srandom((unsigned int)(31415926 + psys->seed));
+
+ if ((psys->renderdata || part->draw_as == PART_DRAW_REND) && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
+ ParticleSimulationData sim = {NULL};
+ sim.scene = scene;
+ sim.ob = par;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(par, psys);
+ /* make sure emitter imat is in global coordinates instead of render view coordinates */
+ invert_m4_m4(par->imat, par->obmat);
+
+ /* first check for loops (particle system object used as dupli object) */
+ if (part->ren_as == PART_DRAW_OB) {
+ if (ELEM(part->dup_ob, NULL, par))
+ return;
+ }
+ else { /*PART_DRAW_GR */
+ if (part->dup_group == NULL || BLI_listbase_is_empty(&part->dup_group->gobject))
+ return;
+
+ if (BLI_findptr(&part->dup_group->gobject, par, offsetof(GroupObject, ob))) {
+ return;
+ }
+ }
+
+ /* if we have a hair particle system, use the path cache */
+ if (part->type == PART_HAIR) {
+ if (psys->flag & PSYS_HAIR_DONE)
+ hair = (totchild == 0 || psys->childcache) && psys->pathcache;
+ if (!hair)
+ return;
+
+ /* we use cache, update totchild according to cached data */
+ totchild = psys->totchildcache;
+ totpart = psys->totcached;
+ }
+
+ psys_check_group_weights(part);
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ /* gather list of objects or single object */
+ if (part->ren_as == PART_DRAW_GR) {
+ if (ctx->do_update) {
+ BKE_group_handle_recalc_and_update(ctx->eval_ctx, scene, par, part->dup_group);
+ }
+
+ if (part->draw & PART_DRAW_COUNT_GR) {
+ for (dw = part->dupliweights.first; dw; dw = dw->next)
+ totgroup += dw->count;
+ }
+ else {
+ for (go = part->dup_group->gobject.first; go; go = go->next)
+ totgroup++;
+ }
+
+ /* we also copy the actual objects to restore afterwards, since
+ * BKE_object_where_is_calc_time will change the object which breaks transform */
+ oblist = MEM_callocN((size_t)totgroup * sizeof(Object *), "dupgroup object list");
+ obcopylist = MEM_callocN((size_t)totgroup * sizeof(Object), "dupgroup copy list");
+
+ if (part->draw & PART_DRAW_COUNT_GR && totgroup) {
+ dw = part->dupliweights.first;
+
+ for (a = 0; a < totgroup; dw = dw->next) {
+ for (b = 0; b < dw->count; b++, a++) {
+ oblist[a] = dw->ob;
+ obcopylist[a] = *dw->ob;
+ }
+ }
+ }
+ else {
+ go = part->dup_group->gobject.first;
+ for (a = 0; a < totgroup; a++, go = go->next) {
+ oblist[a] = go->ob;
+ obcopylist[a] = *go->ob;
+ }
+ }
+ }
+ else {
+ ob = part->dup_ob;
+ obcopy = *ob;
+ }
+
+ if (totchild == 0 || part->draw & PART_DRAW_PARENT)
+ a = 0;
+ else
+ a = totpart;
+
+ for (pa = psys->particles; a < totpart + totchild; a++, pa++) {
+ if (a < totpart) {
+ /* handle parent particle */
+ if (pa->flag & no_draw_flag)
+ continue;
+
+ /* pa_num = pa->num; */ /* UNUSED */
+ pa_time = pa->time;
+ size = pa->size;
+ }
+ else {
+ /* handle child particle */
+ cpa = &psys->child[a - totpart];
+
+ /* pa_num = a; */ /* UNUSED */
+ pa_time = psys->particles[cpa->parent].time;
+ size = psys_get_child_size(psys, cpa, ctime, NULL);
+ }
+
+ /* some hair paths might be non-existent so they can't be used for duplication */
+ if (hair && psys->pathcache &&
+ ((a < totpart && psys->pathcache[a]->segments < 0) ||
+ (a >= totpart && psys->childcache[a - totpart]->segments < 0)))
+ {
+ continue;
+ }
+
+ if (part->ren_as == PART_DRAW_GR) {
+ /* prevent divide by zero below [#28336] */
+ if (totgroup == 0)
+ continue;
+
+ /* for groups, pick the object based on settings */
+ if (part->draw & PART_DRAW_RAND_GR)
+ b = BLI_rand() % totgroup;
+ else
+ b = a % totgroup;
+
+ ob = oblist[b];
+ obmat = oblist[b]->obmat;
+ }
+ else {
+ obmat = ob->obmat;
+ }
+
+ if (hair) {
+ /* hair we handle separate and compute transform based on hair keys */
+ if (a < totpart) {
+ cache = psys->pathcache[a];
+ psys_get_dupli_path_transform(&sim, pa, NULL, cache, pamat, &scale);
+ }
+ else {
+ cache = psys->childcache[a - totpart];
+ psys_get_dupli_path_transform(&sim, NULL, cpa, cache, pamat, &scale);
+ }
+
+ copy_v3_v3(pamat[3], cache->co);
+ pamat[3][3] = 1.0f;
+
+ }
+ else {
+ /* first key */
+ state.time = ctime;
+ if (psys_get_particle_state(&sim, a, &state, 0) == 0) {
+ continue;
+ }
+ else {
+ float tquat[4];
+ normalize_qt_qt(tquat, state.rot);
+ quat_to_mat4(pamat, tquat);
+ copy_v3_v3(pamat[3], state.co);
+ pamat[3][3] = 1.0f;
+ }
+ }
+
+ if (part->ren_as == PART_DRAW_GR && psys->part->draw & PART_DRAW_WHOLE_GR) {
+ for (go = part->dup_group->gobject.first, b = 0; go; go = go->next, b++) {
+
+ copy_m4_m4(tmat, oblist[b]->obmat);
+ /* apply particle scale */
+ mul_mat3_m4_fl(tmat, size * scale);
+ mul_v3_fl(tmat[3], size * scale);
+ /* group dupli offset, should apply after everything else */
+ if (!is_zero_v3(part->dup_group->dupli_ofs))
+ sub_v3_v3(tmat[3], part->dup_group->dupli_ofs);
+ /* individual particle transform */
+ mul_m4_m4m4(mat, pamat, tmat);
+
+ dob = make_dupli(ctx, go->ob, mat, a, false, false);
+ dob->particle_system = psys;
+ if (use_texcoords)
+ psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
+ }
+ }
+ else {
+ /* to give ipos in object correct offset */
+ BKE_object_where_is_calc_time(scene, ob, ctime - pa_time);
+
+ copy_v3_v3(vec, obmat[3]);
+ obmat[3][0] = obmat[3][1] = obmat[3][2] = 0.0f;
+
+ /* particle rotation uses x-axis as the aligned axis, so pre-rotate the object accordingly */
+ if ((part->draw & PART_DRAW_ROTATE_OB) == 0) {
+ float xvec[3], q[4], size_mat[4][4], original_size[3];
+
+ mat4_to_size(original_size, obmat);
+ size_to_mat4(size_mat, original_size);
+
+ xvec[0] = -1.f;
+ xvec[1] = xvec[2] = 0;
+ vec_to_quat(q, xvec, ob->trackflag, ob->upflag);
+ quat_to_mat4(obmat, q);
+ obmat[3][3] = 1.0f;
+
+ /* add scaling if requested */
+ if ((part->draw & PART_DRAW_NO_SCALE_OB) == 0)
+ mul_m4_m4m4(obmat, obmat, size_mat);
+ }
+ else if (part->draw & PART_DRAW_NO_SCALE_OB) {
+ /* remove scaling */
+ float size_mat[4][4], original_size[3];
+
+ mat4_to_size(original_size, obmat);
+ size_to_mat4(size_mat, original_size);
+ invert_m4(size_mat);
+
+ mul_m4_m4m4(obmat, obmat, size_mat);
+ }
+
+ mul_m4_m4m4(tmat, pamat, obmat);
+ mul_mat3_m4_fl(tmat, size * scale);
+
+ copy_m4_m4(mat, tmat);
+
+ if (part->draw & PART_DRAW_GLOBAL_OB)
+ add_v3_v3v3(mat[3], mat[3], vec);
+
+ dob = make_dupli(ctx, ob, mat, a, false, false);
+ dob->particle_system = psys;
+ if (use_texcoords)
+ psys_get_dupli_texture(psys, part, sim.psmd, pa, cpa, dob->uv, dob->orco);
+ /* XXX blender internal needs this to be set to dupligroup to render
+ * groups correctly, but we don't want this hack for cycles */
+ if (dupli_type_hack && ctx->group)
+ dob->type = OB_DUPLIGROUP;
+ }
+ }
+
+ /* restore objects since they were changed in BKE_object_where_is_calc_time */
+ if (part->ren_as == PART_DRAW_GR) {
+ for (a = 0; a < totgroup; a++)
+ *(oblist[a]) = obcopylist[a];
+ }
+ else
+ *ob = obcopy;
+ }
+
+ /* clean up */
+ if (oblist)
+ MEM_freeN(oblist);
+ if (obcopylist)
+ MEM_freeN(obcopylist);
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+}
+
+static void make_duplis_particles(const DupliContext *ctx)
+{
+ ParticleSystem *psys;
+ int psysid;
+
+ /* particle system take up one level in id, the particles another */
+ for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) {
+ /* particles create one more level for persistent psys index */
+ DupliContext pctx;
+ copy_dupli_context(&pctx, ctx, ctx->object, NULL, psysid, false);
+ make_duplis_particle_system(&pctx, psys);
+ }
+}
+
+const DupliGenerator gen_dupli_particles = {
+ OB_DUPLIPARTS, /* type */
+ make_duplis_particles /* make_duplis */
+};
+
/* ------------- */
/* select dupli generator from given context */
@@ -839,7 +1160,10 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
if (ctx->eval_ctx->mode == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) : (restrictflag & OB_RESTRICT_VIEW))
return NULL;
- if (transflag & OB_DUPLIVERTS) {
+ if (transflag & OB_DUPLIPARTS) {
+ return &gen_dupli_particles;
+ }
+ else if (transflag & OB_DUPLIVERTS) {
if (ctx->object->type == OB_MESH) {
return &gen_dupli_verts;
}
@@ -897,8 +1221,12 @@ int count_duplilist(Object *ob)
if (ob->transflag & OB_DUPLIVERTS) {
if (ob->type == OB_MESH) {
if (ob->transflag & OB_DUPLIVERTS) {
+ ParticleSystem *psys = ob->particlesystem.first;
int pdup = 0;
+ for (; psys; psys = psys->next)
+ pdup += psys->totpart;
+
if (pdup == 0) {
Mesh *me = ob->data;
return me->totvert;
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index b8cb8955672..5cb704e4737 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -32,7 +32,6 @@
#include "DNA_group_types.h"
#include "DNA_key_types.h"
#include "DNA_material_types.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
@@ -54,6 +53,7 @@
#include "BKE_lattice.h"
#include "BKE_editmesh.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_material.h"
#include "BKE_image.h"
@@ -257,6 +257,53 @@ void BKE_object_handle_data_update(EvaluationContext *eval_ctx,
else if (ob->type == OB_LAMP)
lamp_drivers_update(scene, ob->data, ctime);
+ /* particles */
+ if (ob != scene->obedit && ob->particlesystem.first) {
+ ParticleSystem *tpsys, *psys;
+ DerivedMesh *dm;
+ ob->transflag &= ~OB_DUPLIPARTS;
+ psys = ob->particlesystem.first;
+ while (psys) {
+ /* ensure this update always happens even if psys is disabled */
+ if (psys->recalc & PSYS_RECALC_TYPE) {
+ psys_changed_type(ob, psys);
+ }
+
+ if (psys_check_enabled(ob, psys, eval_ctx->mode == DAG_EVAL_RENDER)) {
+ /* check use of dupli objects here */
+ if (psys->part && (psys->part->draw_as == PART_DRAW_REND || eval_ctx->mode == DAG_EVAL_RENDER) &&
+ ((psys->part->ren_as == PART_DRAW_OB && psys->part->dup_ob) ||
+ (psys->part->ren_as == PART_DRAW_GR && psys->part->dup_group)))
+ {
+ ob->transflag |= OB_DUPLIPARTS;
+ }
+
+ particle_system_update(scene, ob, psys, (eval_ctx->mode == DAG_EVAL_RENDER));
+ psys = psys->next;
+ }
+ else if (psys->flag & PSYS_DELETE) {
+ tpsys = psys->next;
+ BLI_remlink(&ob->particlesystem, psys);
+ psys_free(ob, psys);
+ psys = tpsys;
+ }
+ else
+ psys = psys->next;
+ }
+
+ if (eval_ctx->mode == DAG_EVAL_RENDER && ob->transflag & OB_DUPLIPARTS) {
+ /* this is to make sure we get render level duplis in groups:
+ * the derivedmesh must be created before init_render_mesh,
+ * since object_duplilist does dupliparticles before that */
+ CustomDataMask data_mask = CD_MASK_BAREMESH | CD_MASK_MFACE | CD_MASK_MTFACE | CD_MASK_MCOL;
+ dm = mesh_create_derived_render(scene, ob, data_mask);
+ dm->release(dm);
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated;
+ }
+ }
+
/* quick cache removed */
}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
new file mode 100644
index 00000000000..1ea27558545
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle.c
@@ -0,0 +1,4304 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/particle.c
+ * \ingroup bke
+ */
+
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_group_types.h"
+#include "DNA_key_types.h"
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_smoke_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_dynamicpaint_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_noise.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_kdtree.h"
+#include "BLI_rand.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
+#include "BLI_linklist.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_anim.h"
+#include "BKE_animsys.h"
+
+#include "BKE_boids.h"
+#include "BKE_cloth.h"
+#include "BKE_colortools.h"
+#include "BKE_effect.h"
+#include "BKE_global.h"
+#include "BKE_group.h"
+#include "BKE_main.h"
+#include "BKE_lattice.h"
+
+#include "BKE_displist.h"
+#include "BKE_particle.h"
+#include "BKE_material.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_library_query.h"
+#include "BKE_library_remap.h"
+#include "BKE_depsgraph.h"
+#include "BKE_modifier.h"
+#include "BKE_mesh.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_pointcache.h"
+#include "BKE_scene.h"
+#include "BKE_deform.h"
+
+#include "RE_render_ext.h"
+
+unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT];
+unsigned int PSYS_FRAND_SEED_MULTIPLIER[PSYS_FRAND_COUNT];
+float PSYS_FRAND_BASE[PSYS_FRAND_COUNT];
+
+void psys_init_rng(void)
+{
+ int i;
+ BLI_srandom(5831); /* arbitrary */
+ for (i = 0; i < PSYS_FRAND_COUNT; ++i) {
+ PSYS_FRAND_BASE[i] = BLI_frand();
+ PSYS_FRAND_SEED_OFFSET[i] = (unsigned int)BLI_rand();
+ PSYS_FRAND_SEED_MULTIPLIER[i] = (unsigned int)BLI_rand();
+ }
+}
+
+static void get_child_modifier_parameters(ParticleSettings *part, ParticleThreadContext *ctx,
+ ChildParticle *cpa, short cpa_from, int cpa_num, float *cpa_fuv, float *orco, ParticleTexture *ptex);
+static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSettings *part, ParticleData *par,
+ int child_index, int face_index, const float fw[4], float *orco, ParticleTexture *ptex, int event, float cfra);
+extern void do_child_modifiers(ParticleThreadContext *ctx, ParticleSimulationData *sim,
+ ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3],
+ ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t);
+
+/* few helpers for countall etc. */
+int count_particles(ParticleSystem *psys)
+{
+ ParticleSettings *part = psys->part;
+ PARTICLE_P;
+ int tot = 0;
+
+ LOOP_SHOWN_PARTICLES {
+ if (pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN) == 0) {}
+ else if (pa->alive == PARS_DEAD && (part->flag & PART_DIED) == 0) {}
+ else tot++;
+ }
+ return tot;
+}
+int count_particles_mod(ParticleSystem *psys, int totgr, int cur)
+{
+ ParticleSettings *part = psys->part;
+ PARTICLE_P;
+ int tot = 0;
+
+ LOOP_SHOWN_PARTICLES {
+ if (pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN) == 0) {}
+ else if (pa->alive == PARS_DEAD && (part->flag & PART_DIED) == 0) {}
+ else if (p % totgr == cur) tot++;
+ }
+ return tot;
+}
+/* we allocate path cache memory in chunks instead of a big contiguous
+ * chunk, windows' memory allocater fails to find big blocks of memory often */
+
+#define PATH_CACHE_BUF_SIZE 1024
+
+static ParticleCacheKey *pcache_key_segment_endpoint_safe(ParticleCacheKey *key)
+{
+ return (key->segments > 0) ? (key + (key->segments - 1)) : key;
+}
+
+static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int totkeys)
+{
+ LinkData *buf;
+ ParticleCacheKey **cache;
+ int i, totkey, totbufkey;
+
+ tot = MAX2(tot, 1);
+ totkey = 0;
+ cache = MEM_callocN(tot * sizeof(void *), "PathCacheArray");
+
+ while (totkey < tot) {
+ totbufkey = MIN2(tot - totkey, PATH_CACHE_BUF_SIZE);
+ buf = MEM_callocN(sizeof(LinkData), "PathCacheLinkData");
+ buf->data = MEM_callocN(sizeof(ParticleCacheKey) * totbufkey * totkeys, "ParticleCacheKey");
+
+ for (i = 0; i < totbufkey; i++)
+ cache[totkey + i] = ((ParticleCacheKey *)buf->data) + i * totkeys;
+
+ totkey += totbufkey;
+ BLI_addtail(bufs, buf);
+ }
+
+ return cache;
+}
+
+static void psys_free_path_cache_buffers(ParticleCacheKey **cache, ListBase *bufs)
+{
+ LinkData *buf;
+
+ if (cache)
+ MEM_freeN(cache);
+
+ for (buf = bufs->first; buf; buf = buf->next)
+ MEM_freeN(buf->data);
+ BLI_freelistN(bufs);
+}
+
+/************************************************/
+/* Getting stuff */
+/************************************************/
+/* get object's active particle system safely */
+ParticleSystem *psys_get_current(Object *ob)
+{
+ ParticleSystem *psys;
+ if (ob == NULL) return NULL;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (psys->flag & PSYS_CURRENT)
+ return psys;
+ }
+
+ return NULL;
+}
+short psys_get_current_num(Object *ob)
+{
+ ParticleSystem *psys;
+ short i;
+
+ if (ob == NULL) return 0;
+
+ for (psys = ob->particlesystem.first, i = 0; psys; psys = psys->next, i++)
+ if (psys->flag & PSYS_CURRENT)
+ return i;
+
+ return i;
+}
+void psys_set_current_num(Object *ob, int index)
+{
+ ParticleSystem *psys;
+ short i;
+
+ if (ob == NULL) return;
+
+ for (psys = ob->particlesystem.first, i = 0; psys; psys = psys->next, i++) {
+ if (i == index)
+ psys->flag |= PSYS_CURRENT;
+ else
+ psys->flag &= ~PSYS_CURRENT;
+ }
+}
+
+#if 0 /* UNUSED */
+Object *psys_find_object(Scene *scene, ParticleSystem *psys)
+{
+ Base *base;
+ ParticleSystem *tpsys;
+
+ for (base = scene->base.first; base; base = base->next) {
+ for (tpsys = base->object->particlesystem.first; psys; psys = psys->next) {
+ if (tpsys == psys)
+ return base->object;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+struct LatticeDeformData *psys_create_lattice_deform_data(ParticleSimulationData *sim)
+{
+ struct LatticeDeformData *lattice_deform_data = NULL;
+
+ if (psys_in_edit_mode(sim->scene, sim->psys) == 0) {
+ Object *lattice = NULL;
+ ModifierData *md = (ModifierData *)psys_get_modifier(sim->ob, sim->psys);
+
+ for (; md; md = md->next) {
+ if (md->type == eModifierType_Lattice) {
+ LatticeModifierData *lmd = (LatticeModifierData *)md;
+ lattice = lmd->object;
+ break;
+ }
+ }
+ if (lattice)
+ lattice_deform_data = init_latt_deform(lattice, NULL);
+ }
+
+ return lattice_deform_data;
+}
+void psys_disable_all(Object *ob)
+{
+ ParticleSystem *psys = ob->particlesystem.first;
+
+ for (; psys; psys = psys->next)
+ psys->flag |= PSYS_DISABLED;
+}
+void psys_enable_all(Object *ob)
+{
+ ParticleSystem *psys = ob->particlesystem.first;
+
+ for (; psys; psys = psys->next)
+ psys->flag &= ~PSYS_DISABLED;
+}
+bool psys_in_edit_mode(Scene *scene, ParticleSystem *psys)
+{
+ return (scene->basact && (scene->basact->object->mode & OB_MODE_PARTICLE_EDIT) && psys == psys_get_current((scene->basact)->object) && (psys->edit || psys->pointcache->edit) && !psys->renderdata);
+}
+bool psys_check_enabled(Object *ob, ParticleSystem *psys, const bool use_render_params)
+{
+ ParticleSystemModifierData *psmd;
+
+ if (psys->flag & PSYS_DISABLED || psys->flag & PSYS_DELETE || !psys->part)
+ return 0;
+
+ psmd = psys_get_modifier(ob, psys);
+ if (psys->renderdata || use_render_params) {
+ if (!(psmd->modifier.mode & eModifierMode_Render))
+ return 0;
+ }
+ else if (!(psmd->modifier.mode & eModifierMode_Realtime))
+ return 0;
+
+ return 1;
+}
+
+bool psys_check_edited(ParticleSystem *psys)
+{
+ if (psys->part && psys->part->type == PART_HAIR)
+ return (psys->flag & PSYS_EDITED || (psys->edit && psys->edit->edited));
+ else
+ return (psys->pointcache->edit && psys->pointcache->edit->edited);
+}
+
+void psys_check_group_weights(ParticleSettings *part)
+{
+ ParticleDupliWeight *dw, *tdw;
+ GroupObject *go;
+ int current = 0;
+
+ if (part->ren_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first) {
+ /* First try to find NULL objects from their index,
+ * and remove all weights that don't have an object in the group. */
+ dw = part->dupliweights.first;
+ while (dw) {
+ if (dw->ob == NULL || !BKE_group_object_exists(part->dup_group, dw->ob)) {
+ go = (GroupObject *)BLI_findlink(&part->dup_group->gobject, dw->index);
+ if (go) {
+ dw->ob = go->ob;
+ }
+ else {
+ tdw = dw->next;
+ BLI_freelinkN(&part->dupliweights, dw);
+ dw = tdw;
+ }
+ }
+ else {
+ dw = dw->next;
+ }
+ }
+
+ /* then add objects in the group to new list */
+ go = part->dup_group->gobject.first;
+ while (go) {
+ dw = part->dupliweights.first;
+ while (dw && dw->ob != go->ob)
+ dw = dw->next;
+
+ if (!dw) {
+ dw = MEM_callocN(sizeof(ParticleDupliWeight), "ParticleDupliWeight");
+ dw->ob = go->ob;
+ dw->count = 1;
+ BLI_addtail(&part->dupliweights, dw);
+ }
+
+ go = go->next;
+ }
+
+ dw = part->dupliweights.first;
+ for (; dw; dw = dw->next) {
+ if (dw->flag & PART_DUPLIW_CURRENT) {
+ current = 1;
+ break;
+ }
+ }
+
+ if (!current) {
+ dw = part->dupliweights.first;
+ if (dw)
+ dw->flag |= PART_DUPLIW_CURRENT;
+ }
+ }
+ else {
+ BLI_freelistN(&part->dupliweights);
+ }
+}
+int psys_uses_gravity(ParticleSimulationData *sim)
+{
+ return sim->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY && sim->psys->part && sim->psys->part->effector_weights->global_gravity != 0.0f;
+}
+/************************************************/
+/* Freeing stuff */
+/************************************************/
+static void fluid_free_settings(SPHFluidSettings *fluid)
+{
+ if (fluid)
+ MEM_freeN(fluid);
+}
+
+/** Free (or release) any data used by this particle settings (does not free the partsett itself). */
+void BKE_particlesettings_free(ParticleSettings *part)
+{
+ int a;
+
+ BKE_animdata_free((ID *)part, false);
+
+ for (a = 0; a < MAX_MTEX; a++) {
+ MEM_SAFE_FREE(part->mtex[a]);
+ }
+
+ if (part->clumpcurve)
+ curvemapping_free(part->clumpcurve);
+ if (part->roughcurve)
+ curvemapping_free(part->roughcurve);
+
+ free_partdeflect(part->pd);
+ free_partdeflect(part->pd2);
+
+ MEM_SAFE_FREE(part->effector_weights);
+
+ BLI_freelistN(&part->dupliweights);
+
+ boid_free_settings(part->boids);
+ fluid_free_settings(part->fluid);
+}
+
+void free_hair(Object *UNUSED(ob), ParticleSystem *psys, int dynamics)
+{
+ PARTICLE_P;
+
+ LOOP_PARTICLES {
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ pa->hair = NULL;
+ pa->totkey = 0;
+ }
+
+ psys->flag &= ~PSYS_HAIR_DONE;
+
+ if (psys->clmd) {
+ if (dynamics) {
+ BKE_ptcache_free_list(&psys->ptcaches);
+ psys->pointcache = NULL;
+
+ modifier_free((ModifierData *)psys->clmd);
+
+ psys->clmd = NULL;
+ psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
+ }
+ else {
+ cloth_free_modifier(psys->clmd);
+ }
+ }
+
+ if (psys->hair_in_dm)
+ psys->hair_in_dm->release(psys->hair_in_dm);
+ psys->hair_in_dm = NULL;
+
+ if (psys->hair_out_dm)
+ psys->hair_out_dm->release(psys->hair_out_dm);
+ psys->hair_out_dm = NULL;
+}
+void free_keyed_keys(ParticleSystem *psys)
+{
+ PARTICLE_P;
+
+ if (psys->part->type == PART_HAIR)
+ return;
+
+ if (psys->particles && psys->particles->keys) {
+ MEM_freeN(psys->particles->keys);
+
+ LOOP_PARTICLES {
+ if (pa->keys) {
+ pa->keys = NULL;
+ pa->totkey = 0;
+ }
+ }
+ }
+}
+static void free_child_path_cache(ParticleSystem *psys)
+{
+ psys_free_path_cache_buffers(psys->childcache, &psys->childcachebufs);
+ psys->childcache = NULL;
+ psys->totchildcache = 0;
+}
+void psys_free_path_cache(ParticleSystem *psys, PTCacheEdit *edit)
+{
+ if (edit) {
+ psys_free_path_cache_buffers(edit->pathcache, &edit->pathcachebufs);
+ edit->pathcache = NULL;
+ edit->totcached = 0;
+ }
+ if (psys) {
+ psys_free_path_cache_buffers(psys->pathcache, &psys->pathcachebufs);
+ psys->pathcache = NULL;
+ psys->totcached = 0;
+
+ free_child_path_cache(psys);
+ }
+}
+void psys_free_children(ParticleSystem *psys)
+{
+ if (psys->child) {
+ MEM_freeN(psys->child);
+ psys->child = NULL;
+ psys->totchild = 0;
+ }
+
+ free_child_path_cache(psys);
+}
+void psys_free_particles(ParticleSystem *psys)
+{
+ PARTICLE_P;
+
+ if (psys->particles) {
+ /* Even though psys->part should never be NULL, this can happen as an exception during deletion.
+ * See ID_REMAP_SKIP/FORCE/FLAG_NEVER_NULL_USAGE in BKE_library_remap. */
+ if (psys->part && psys->part->type == PART_HAIR) {
+ LOOP_PARTICLES {
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ }
+ }
+
+ if (psys->particles->keys)
+ MEM_freeN(psys->particles->keys);
+
+ if (psys->particles->boid)
+ MEM_freeN(psys->particles->boid);
+
+ MEM_freeN(psys->particles);
+ psys->particles = NULL;
+ psys->totpart = 0;
+ }
+}
+void psys_free_pdd(ParticleSystem *psys)
+{
+ if (psys->pdd) {
+ if (psys->pdd->cdata)
+ MEM_freeN(psys->pdd->cdata);
+ psys->pdd->cdata = NULL;
+
+ if (psys->pdd->vdata)
+ MEM_freeN(psys->pdd->vdata);
+ psys->pdd->vdata = NULL;
+
+ if (psys->pdd->ndata)
+ MEM_freeN(psys->pdd->ndata);
+ psys->pdd->ndata = NULL;
+
+ if (psys->pdd->vedata)
+ MEM_freeN(psys->pdd->vedata);
+ psys->pdd->vedata = NULL;
+
+ psys->pdd->totpoint = 0;
+ psys->pdd->tot_vec_size = 0;
+ }
+}
+/* free everything */
+void psys_free(Object *ob, ParticleSystem *psys)
+{
+ if (psys) {
+ int nr = 0;
+ ParticleSystem *tpsys;
+
+ psys_free_path_cache(psys, NULL);
+
+ free_hair(ob, psys, 1);
+
+ psys_free_particles(psys);
+
+ if (psys->edit && psys->free_edit)
+ psys->free_edit(psys->edit);
+
+ if (psys->child) {
+ MEM_freeN(psys->child);
+ psys->child = NULL;
+ psys->totchild = 0;
+ }
+
+ /* check if we are last non-visible particle system */
+ for (tpsys = ob->particlesystem.first; tpsys; tpsys = tpsys->next) {
+ if (tpsys->part) {
+ if (ELEM(tpsys->part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
+ nr++;
+ break;
+ }
+ }
+ }
+ /* clear do-not-draw-flag */
+ if (!nr)
+ ob->transflag &= ~OB_DUPLIPARTS;
+
+ psys->part = NULL;
+
+ BKE_ptcache_free_list(&psys->ptcaches);
+ psys->pointcache = NULL;
+
+ BLI_freelistN(&psys->targets);
+
+ BLI_bvhtree_free(psys->bvhtree);
+ BLI_kdtree_free(psys->tree);
+
+ if (psys->fluid_springs)
+ MEM_freeN(psys->fluid_springs);
+
+ pdEndEffectors(&psys->effectors);
+
+ if (psys->pdd) {
+ psys_free_pdd(psys);
+ MEM_freeN(psys->pdd);
+ }
+
+ MEM_freeN(psys);
+ }
+}
+
+/************************************************/
+/* Rendering */
+/************************************************/
+/* these functions move away particle data and bring it back after
+ * rendering, to make different render settings possible without
+ * removing the previous data. this should be solved properly once */
+
+void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[4][4], float winmat[4][4], int winx, int winy, int timeoffset)
+{
+ ParticleRenderData *data;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+
+ if (psys->renderdata)
+ return;
+
+ data = MEM_callocN(sizeof(ParticleRenderData), "ParticleRenderData");
+
+ data->child = psys->child;
+ data->totchild = psys->totchild;
+ data->pathcache = psys->pathcache;
+ data->pathcachebufs.first = psys->pathcachebufs.first;
+ data->pathcachebufs.last = psys->pathcachebufs.last;
+ data->totcached = psys->totcached;
+ data->childcache = psys->childcache;
+ data->childcachebufs.first = psys->childcachebufs.first;
+ data->childcachebufs.last = psys->childcachebufs.last;
+ data->totchildcache = psys->totchildcache;
+
+ if (psmd->dm_final)
+ data->dm = CDDM_copy(psmd->dm_final);
+ data->totdmvert = psmd->totdmvert;
+ data->totdmedge = psmd->totdmedge;
+ data->totdmface = psmd->totdmface;
+
+ psys->child = NULL;
+ psys->pathcache = NULL;
+ psys->childcache = NULL;
+ psys->totchild = psys->totcached = psys->totchildcache = 0;
+ BLI_listbase_clear(&psys->pathcachebufs);
+ BLI_listbase_clear(&psys->childcachebufs);
+
+ copy_m4_m4(data->winmat, winmat);
+ mul_m4_m4m4(data->viewmat, viewmat, ob->obmat);
+ mul_m4_m4m4(data->mat, winmat, data->viewmat);
+ data->winx = winx;
+ data->winy = winy;
+
+ data->timeoffset = timeoffset;
+
+ psys->renderdata = data;
+
+ /* Hair can and has to be recalculated if everything isn't displayed. */
+ if (psys->part->disp != 100 && psys->part->type == PART_HAIR)
+ psys->recalc |= PSYS_RECALC_RESET;
+}
+
+void psys_render_restore(Object *ob, ParticleSystem *psys)
+{
+ ParticleRenderData *data;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ float render_disp = psys_get_current_display_percentage(psys);
+ float disp;
+
+ data = psys->renderdata;
+ if (!data)
+ return;
+
+ if (data->elems)
+ MEM_freeN(data->elems);
+
+ if (psmd->dm_final) {
+ psmd->dm_final->needsFree = 1;
+ psmd->dm_final->release(psmd->dm_final);
+ }
+ if (psmd->dm_deformed) {
+ psmd->dm_deformed->needsFree = 1;
+ psmd->dm_deformed->release(psmd->dm_deformed);
+ psmd->dm_deformed = NULL;
+ }
+
+ psys_free_path_cache(psys, NULL);
+
+ if (psys->child) {
+ MEM_freeN(psys->child);
+ psys->child = 0;
+ psys->totchild = 0;
+ }
+
+ psys->child = data->child;
+ psys->totchild = data->totchild;
+ psys->pathcache = data->pathcache;
+ psys->pathcachebufs.first = data->pathcachebufs.first;
+ psys->pathcachebufs.last = data->pathcachebufs.last;
+ psys->totcached = data->totcached;
+ psys->childcache = data->childcache;
+ psys->childcachebufs.first = data->childcachebufs.first;
+ psys->childcachebufs.last = data->childcachebufs.last;
+ psys->totchildcache = data->totchildcache;
+
+ psmd->dm_final = data->dm;
+ psmd->totdmvert = data->totdmvert;
+ psmd->totdmedge = data->totdmedge;
+ psmd->totdmface = data->totdmface;
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+
+ if (psmd->dm_final) {
+ if (!psmd->dm_final->deformedOnly) {
+ if (ob->derivedDeform) {
+ psmd->dm_deformed = CDDM_copy(ob->derivedDeform);
+ }
+ else {
+ psmd->dm_deformed = CDDM_from_mesh((Mesh *)ob->data);
+ }
+ DM_ensure_tessface(psmd->dm_deformed);
+ }
+ psys_calc_dmcache(ob, psmd->dm_final, psmd->dm_deformed, psys);
+ }
+
+ MEM_freeN(data);
+ psys->renderdata = NULL;
+
+ /* restore particle display percentage */
+ disp = psys_get_current_display_percentage(psys);
+
+ if (disp != render_disp) {
+ /* Hair can and has to be recalculated if everything isn't displayed. */
+ if (psys->part->type == PART_HAIR) {
+ psys->recalc |= PSYS_RECALC_RESET;
+ }
+ else {
+ PARTICLE_P;
+
+ LOOP_PARTICLES {
+ if (psys_frand(psys, p) > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+ }
+ }
+}
+
+bool psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float *params)
+{
+ ParticleRenderData *data;
+ ParticleRenderElem *elem;
+ float x, w, scale, alpha, lambda, t, scalemin, scalemax;
+ int b;
+
+ if (!(psys->renderdata && (psys->part->simplify_flag & PART_SIMPLIFY_ENABLE)))
+ return false;
+
+ data = psys->renderdata;
+ if (!data->do_simplify)
+ return false;
+ b = (data->index_mf_to_mpoly) ? DM_origindex_mface_mpoly(data->index_mf_to_mpoly, data->index_mp_to_orig, cpa->num) : cpa->num;
+ if (b == ORIGINDEX_NONE) {
+ return false;
+ }
+
+ elem = &data->elems[b];
+
+ lambda = elem->lambda;
+ t = elem->t;
+ scalemin = elem->scalemin;
+ scalemax = elem->scalemax;
+
+ if (!elem->reduce) {
+ scale = scalemin;
+ alpha = 1.0f;
+ }
+ else {
+ x = (elem->curchild + 0.5f) / elem->totchild;
+ if (x < lambda - t) {
+ scale = scalemax;
+ alpha = 1.0f;
+ }
+ else if (x >= lambda + t) {
+ scale = scalemin;
+ alpha = 0.0f;
+ }
+ else {
+ w = (lambda + t - x) / (2.0f * t);
+ scale = scalemin + (scalemax - scalemin) * w;
+ alpha = w;
+ }
+ }
+
+ params[0] = scale;
+ params[1] = alpha;
+
+ elem->curchild++;
+
+ return 1;
+}
+
+/************************************************/
+/* Interpolation */
+/************************************************/
+static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four)
+{
+ float value;
+
+ value = w[0] * v1 + w[1] * v2 + w[2] * v3;
+ if (four)
+ value += w[3] * v4;
+
+ CLAMP(value, 0.f, 1.f);
+
+ return value;
+}
+
+void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, bool velocity)
+{
+ float t[4];
+
+ if (type < 0) {
+ interp_cubic_v3(result->co, result->vel, keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt);
+ }
+ else {
+ key_curve_position_weights(dt, t, type);
+
+ interp_v3_v3v3v3v3(result->co, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
+
+ if (velocity) {
+ float temp[3];
+
+ if (dt > 0.999f) {
+ key_curve_position_weights(dt - 0.001f, t, type);
+ interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
+ sub_v3_v3v3(result->vel, result->co, temp);
+ }
+ else {
+ key_curve_position_weights(dt + 0.001f, t, type);
+ interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
+ sub_v3_v3v3(result->vel, temp, result->co);
+ }
+ }
+ }
+}
+
+
+typedef struct ParticleInterpolationData {
+ HairKey *hkey[2];
+
+ DerivedMesh *dm;
+ MVert *mvert[2];
+
+ int keyed;
+ ParticleKey *kkey[2];
+
+ PointCache *cache;
+ PTCacheMem *pm;
+
+ PTCacheEditPoint *epoint;
+ PTCacheEditKey *ekey[2];
+
+ float birthtime, dietime;
+ int bspline;
+} ParticleInterpolationData;
+/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */
+/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */
+static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2)
+{
+ static PTCacheMem *pm = NULL;
+ int index1, index2;
+
+ if (index < 0) { /* initialize */
+ *cur = cache->mem_cache.first;
+
+ if (*cur)
+ *cur = (*cur)->next;
+ }
+ else {
+ if (*cur) {
+ while (*cur && (*cur)->next && (float)(*cur)->frame < t)
+ *cur = (*cur)->next;
+
+ pm = *cur;
+
+ index2 = BKE_ptcache_mem_index_find(pm, index);
+ index1 = BKE_ptcache_mem_index_find(pm->prev, index);
+ if (index2 < 0) {
+ return;
+ }
+
+ BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame);
+ if (index1 < 0)
+ copy_particle_key(key1, key2, 1);
+ else
+ BKE_ptcache_make_particle_key(key1, index1, pm->prev->data, (float)pm->prev->frame);
+ }
+ else if (cache->mem_cache.first) {
+ pm = cache->mem_cache.first;
+ index2 = BKE_ptcache_mem_index_find(pm, index);
+ if (index2 < 0) {
+ return;
+ }
+ BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame);
+ copy_particle_key(key1, key2, 1);
+ }
+ }
+}
+static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end)
+{
+ PTCacheMem *pm;
+ int ret = 0;
+
+ for (pm = cache->mem_cache.first; pm; pm = pm->next) {
+ if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
+ *start = pm->frame;
+ ret++;
+ break;
+ }
+ }
+
+ for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
+ if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
+ *end = pm->frame;
+ ret++;
+ break;
+ }
+ }
+
+ return ret == 2;
+}
+
+float psys_get_dietime_from_cache(PointCache *cache, int index)
+{
+ PTCacheMem *pm;
+ int dietime = 10000000; /* some max value so that we can default to pa->time+lifetime */
+
+ for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
+ if (BKE_ptcache_mem_index_find(pm, index) >= 0)
+ return (float)pm->frame;
+ }
+
+ return (float)dietime;
+}
+
+static void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind)
+{
+
+ if (pind->epoint) {
+ PTCacheEditPoint *point = pind->epoint;
+
+ pind->ekey[0] = point->keys;
+ pind->ekey[1] = point->totkey > 1 ? point->keys + 1 : NULL;
+
+ pind->birthtime = *(point->keys->time);
+ pind->dietime = *((point->keys + point->totkey - 1)->time);
+ }
+ else if (pind->keyed) {
+ ParticleKey *key = pa->keys;
+ pind->kkey[0] = key;
+ pind->kkey[1] = pa->totkey > 1 ? key + 1 : NULL;
+
+ pind->birthtime = key->time;
+ pind->dietime = (key + pa->totkey - 1)->time;
+ }
+ else if (pind->cache) {
+ float start = 0.0f, end = 0.0f;
+ get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL);
+ pind->birthtime = pa ? pa->time : pind->cache->startframe;
+ pind->dietime = pa ? pa->dietime : pind->cache->endframe;
+
+ if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) {
+ pind->birthtime = MAX2(pind->birthtime, start);
+ pind->dietime = MIN2(pind->dietime, end);
+ }
+ }
+ else {
+ HairKey *key = pa->hair;
+ pind->hkey[0] = key;
+ pind->hkey[1] = key + 1;
+
+ pind->birthtime = key->time;
+ pind->dietime = (key + pa->totkey - 1)->time;
+
+ if (pind->dm) {
+ pind->mvert[0] = CDDM_get_vert(pind->dm, pa->hair_index);
+ pind->mvert[1] = pind->mvert[0] + 1;
+ }
+ }
+}
+static void edit_to_particle(ParticleKey *key, PTCacheEditKey *ekey)
+{
+ copy_v3_v3(key->co, ekey->co);
+ if (ekey->vel) {
+ copy_v3_v3(key->vel, ekey->vel);
+ }
+ key->time = *(ekey->time);
+}
+static void hair_to_particle(ParticleKey *key, HairKey *hkey)
+{
+ copy_v3_v3(key->co, hkey->co);
+ key->time = hkey->time;
+}
+
+static void mvert_to_particle(ParticleKey *key, MVert *mvert, HairKey *hkey)
+{
+ copy_v3_v3(key->co, mvert->co);
+ key->time = hkey->time;
+}
+
+static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, ParticleInterpolationData *pind, ParticleKey *result)
+{
+ PTCacheEditPoint *point = pind->epoint;
+ ParticleKey keys[4];
+ int point_vel = (point && point->keys->vel);
+ float real_t, dfra, keytime, invdt = 1.f;
+
+ /* billboards wont fill in all of these, so start cleared */
+ memset(keys, 0, sizeof(keys));
+
+ /* interpret timing and find keys */
+ if (point) {
+ if (result->time < 0.0f)
+ real_t = -result->time;
+ else
+ real_t = *(pind->ekey[0]->time) + t * (*(pind->ekey[0][point->totkey - 1].time) - *(pind->ekey[0]->time));
+
+ while (*(pind->ekey[1]->time) < real_t)
+ pind->ekey[1]++;
+
+ pind->ekey[0] = pind->ekey[1] - 1;
+ }
+ else if (pind->keyed) {
+ /* we have only one key, so let's use that */
+ if (pind->kkey[1] == NULL) {
+ copy_particle_key(result, pind->kkey[0], 1);
+ return;
+ }
+
+ if (result->time < 0.0f)
+ real_t = -result->time;
+ else
+ real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey - 1].time - pind->kkey[0]->time);
+
+ if (psys->part->phystype == PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) {
+ ParticleTarget *pt = psys->targets.first;
+
+ pt = pt->next;
+
+ while (pt && pa->time + pt->time < real_t)
+ pt = pt->next;
+
+ if (pt) {
+ pt = pt->prev;
+
+ if (pa->time + pt->time + pt->duration > real_t)
+ real_t = pa->time + pt->time;
+ }
+ else
+ real_t = pa->time + ((ParticleTarget *)psys->targets.last)->time;
+ }
+
+ CLAMP(real_t, pa->time, pa->dietime);
+
+ while (pind->kkey[1]->time < real_t)
+ pind->kkey[1]++;
+
+ pind->kkey[0] = pind->kkey[1] - 1;
+ }
+ else if (pind->cache) {
+ if (result->time < 0.0f) /* flag for time in frames */
+ real_t = -result->time;
+ else
+ real_t = pa->time + t * (pa->dietime - pa->time);
+ }
+ else {
+ if (result->time < 0.0f)
+ real_t = -result->time;
+ else
+ real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey - 1].time - pind->hkey[0]->time);
+
+ while (pind->hkey[1]->time < real_t) {
+ pind->hkey[1]++;
+ pind->mvert[1]++;
+ }
+
+ pind->hkey[0] = pind->hkey[1] - 1;
+ }
+
+ /* set actual interpolation keys */
+ if (point) {
+ edit_to_particle(keys + 1, pind->ekey[0]);
+ edit_to_particle(keys + 2, pind->ekey[1]);
+ }
+ else if (pind->dm) {
+ pind->mvert[0] = pind->mvert[1] - 1;
+ mvert_to_particle(keys + 1, pind->mvert[0], pind->hkey[0]);
+ mvert_to_particle(keys + 2, pind->mvert[1], pind->hkey[1]);
+ }
+ else if (pind->keyed) {
+ memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey));
+ memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey));
+ }
+ else if (pind->cache) {
+ get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys + 1, keys + 2);
+ }
+ else {
+ hair_to_particle(keys + 1, pind->hkey[0]);
+ hair_to_particle(keys + 2, pind->hkey[1]);
+ }
+
+ /* set secondary interpolation keys for hair */
+ if (!pind->keyed && !pind->cache && !point_vel) {
+ if (point) {
+ if (pind->ekey[0] != point->keys)
+ edit_to_particle(keys, pind->ekey[0] - 1);
+ else
+ edit_to_particle(keys, pind->ekey[0]);
+ }
+ else if (pind->dm) {
+ if (pind->hkey[0] != pa->hair)
+ mvert_to_particle(keys, pind->mvert[0] - 1, pind->hkey[0] - 1);
+ else
+ mvert_to_particle(keys, pind->mvert[0], pind->hkey[0]);
+ }
+ else {
+ if (pind->hkey[0] != pa->hair)
+ hair_to_particle(keys, pind->hkey[0] - 1);
+ else
+ hair_to_particle(keys, pind->hkey[0]);
+ }
+
+ if (point) {
+ if (pind->ekey[1] != point->keys + point->totkey - 1)
+ edit_to_particle(keys + 3, pind->ekey[1] + 1);
+ else
+ edit_to_particle(keys + 3, pind->ekey[1]);
+ }
+ else if (pind->dm) {
+ if (pind->hkey[1] != pa->hair + pa->totkey - 1)
+ mvert_to_particle(keys + 3, pind->mvert[1] + 1, pind->hkey[1] + 1);
+ else
+ mvert_to_particle(keys + 3, pind->mvert[1], pind->hkey[1]);
+ }
+ else {
+ if (pind->hkey[1] != pa->hair + pa->totkey - 1)
+ hair_to_particle(keys + 3, pind->hkey[1] + 1);
+ else
+ hair_to_particle(keys + 3, pind->hkey[1]);
+ }
+ }
+
+ dfra = keys[2].time - keys[1].time;
+ keytime = (real_t - keys[1].time) / dfra;
+
+ /* convert velocity to timestep size */
+ if (pind->keyed || pind->cache || point_vel) {
+ invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f);
+ mul_v3_fl(keys[1].vel, invdt);
+ mul_v3_fl(keys[2].vel, invdt);
+ interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime);
+ }
+
+ /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation)*/
+ psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */
+ : (pind->bspline ? KEY_BSPLINE : KEY_CARDINAL),
+ keys, keytime, result, 1);
+
+ /* the velocity needs to be converted back from cubic interpolation */
+ if (pind->keyed || pind->cache || point_vel)
+ mul_v3_fl(result->vel, 1.f / invdt);
+}
+
+static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result)
+{
+ int i = 0;
+ ParticleCacheKey *cur = first;
+
+ /* scale the requested time to fit the entire path even if the path is cut early */
+ t *= (first + first->segments)->time;
+
+ while (i < first->segments && cur->time < t)
+ cur++;
+
+ if (cur->time == t)
+ *result = *cur;
+ else {
+ float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time);
+ interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt);
+ interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt);
+ interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt);
+ result->time = t;
+ }
+
+ /* first is actual base rotation, others are incremental from first */
+ if (cur == first || cur - 1 == first)
+ copy_qt_qt(result->rot, first->rot);
+ else
+ mul_qt_qtqt(result->rot, first->rot, result->rot);
+}
+
+/************************************************/
+/* Particles on a dm */
+/************************************************/
+/* interpolate a location on a face based on face coordinates */
+void psys_interpolate_face(MVert *mvert, MFace *mface, MTFace *tface, float (*orcodata)[3],
+ float w[4], float vec[3], float nor[3], float utan[3], float vtan[3],
+ float orco[3], float ornor[3])
+{
+ float *v1 = 0, *v2 = 0, *v3 = 0, *v4 = 0;
+ float e1[3], e2[3], s1, s2, t1, t2;
+ float *uv1, *uv2, *uv3, *uv4;
+ float n1[3], n2[3], n3[3], n4[3];
+ float tuv[4][2];
+ float *o1, *o2, *o3, *o4;
+
+ v1 = mvert[mface->v1].co;
+ v2 = mvert[mface->v2].co;
+ v3 = mvert[mface->v3].co;
+
+ normal_short_to_float_v3(n1, mvert[mface->v1].no);
+ normal_short_to_float_v3(n2, mvert[mface->v2].no);
+ normal_short_to_float_v3(n3, mvert[mface->v3].no);
+
+ if (mface->v4) {
+ v4 = mvert[mface->v4].co;
+ normal_short_to_float_v3(n4, mvert[mface->v4].no);
+
+ interp_v3_v3v3v3v3(vec, v1, v2, v3, v4, w);
+
+ if (nor) {
+ if (mface->flag & ME_SMOOTH)
+ interp_v3_v3v3v3v3(nor, n1, n2, n3, n4, w);
+ else
+ normal_quad_v3(nor, v1, v2, v3, v4);
+ }
+ }
+ else {
+ interp_v3_v3v3v3(vec, v1, v2, v3, w);
+
+ if (nor) {
+ if (mface->flag & ME_SMOOTH)
+ interp_v3_v3v3v3(nor, n1, n2, n3, w);
+ else
+ normal_tri_v3(nor, v1, v2, v3);
+ }
+ }
+
+ /* calculate tangent vectors */
+ if (utan && vtan) {
+ if (tface) {
+ uv1 = tface->uv[0];
+ uv2 = tface->uv[1];
+ uv3 = tface->uv[2];
+ uv4 = tface->uv[3];
+ }
+ else {
+ uv1 = tuv[0]; uv2 = tuv[1]; uv3 = tuv[2]; uv4 = tuv[3];
+ map_to_sphere(uv1, uv1 + 1, v1[0], v1[1], v1[2]);
+ map_to_sphere(uv2, uv2 + 1, v2[0], v2[1], v2[2]);
+ map_to_sphere(uv3, uv3 + 1, v3[0], v3[1], v3[2]);
+ if (v4)
+ map_to_sphere(uv4, uv4 + 1, v4[0], v4[1], v4[2]);
+ }
+
+ if (v4) {
+ s1 = uv3[0] - uv1[0];
+ s2 = uv4[0] - uv1[0];
+
+ t1 = uv3[1] - uv1[1];
+ t2 = uv4[1] - uv1[1];
+
+ sub_v3_v3v3(e1, v3, v1);
+ sub_v3_v3v3(e2, v4, v1);
+ }
+ else {
+ s1 = uv2[0] - uv1[0];
+ s2 = uv3[0] - uv1[0];
+
+ t1 = uv2[1] - uv1[1];
+ t2 = uv3[1] - uv1[1];
+
+ sub_v3_v3v3(e1, v2, v1);
+ sub_v3_v3v3(e2, v3, v1);
+ }
+
+ vtan[0] = (s1 * e2[0] - s2 * e1[0]);
+ vtan[1] = (s1 * e2[1] - s2 * e1[1]);
+ vtan[2] = (s1 * e2[2] - s2 * e1[2]);
+
+ utan[0] = (t1 * e2[0] - t2 * e1[0]);
+ utan[1] = (t1 * e2[1] - t2 * e1[1]);
+ utan[2] = (t1 * e2[2] - t2 * e1[2]);
+ }
+
+ if (orco) {
+ if (orcodata) {
+ o1 = orcodata[mface->v1];
+ o2 = orcodata[mface->v2];
+ o3 = orcodata[mface->v3];
+
+ if (mface->v4) {
+ o4 = orcodata[mface->v4];
+
+ interp_v3_v3v3v3v3(orco, o1, o2, o3, o4, w);
+
+ if (ornor)
+ normal_quad_v3(ornor, o1, o2, o3, o4);
+ }
+ else {
+ interp_v3_v3v3v3(orco, o1, o2, o3, w);
+
+ if (ornor)
+ normal_tri_v3(ornor, o1, o2, o3);
+ }
+ }
+ else {
+ copy_v3_v3(orco, vec);
+ if (ornor && nor)
+ copy_v3_v3(ornor, nor);
+ }
+ }
+}
+void psys_interpolate_uvs(const MTFace *tface, int quad, const float w[4], float uvco[2])
+{
+ float v10 = tface->uv[0][0];
+ float v11 = tface->uv[0][1];
+ float v20 = tface->uv[1][0];
+ float v21 = tface->uv[1][1];
+ float v30 = tface->uv[2][0];
+ float v31 = tface->uv[2][1];
+ float v40, v41;
+
+ if (quad) {
+ v40 = tface->uv[3][0];
+ v41 = tface->uv[3][1];
+
+ uvco[0] = w[0] * v10 + w[1] * v20 + w[2] * v30 + w[3] * v40;
+ uvco[1] = w[0] * v11 + w[1] * v21 + w[2] * v31 + w[3] * v41;
+ }
+ else {
+ uvco[0] = w[0] * v10 + w[1] * v20 + w[2] * v30;
+ uvco[1] = w[0] * v11 + w[1] * v21 + w[2] * v31;
+ }
+}
+
+void psys_interpolate_mcol(const MCol *mcol, int quad, const float w[4], MCol *mc)
+{
+ const char *cp1, *cp2, *cp3, *cp4;
+ char *cp;
+
+ cp = (char *)mc;
+ cp1 = (const char *)&mcol[0];
+ cp2 = (const char *)&mcol[1];
+ cp3 = (const char *)&mcol[2];
+
+ if (quad) {
+ cp4 = (char *)&mcol[3];
+
+ cp[0] = (int)(w[0] * cp1[0] + w[1] * cp2[0] + w[2] * cp3[0] + w[3] * cp4[0]);
+ cp[1] = (int)(w[0] * cp1[1] + w[1] * cp2[1] + w[2] * cp3[1] + w[3] * cp4[1]);
+ cp[2] = (int)(w[0] * cp1[2] + w[1] * cp2[2] + w[2] * cp3[2] + w[3] * cp4[2]);
+ cp[3] = (int)(w[0] * cp1[3] + w[1] * cp2[3] + w[2] * cp3[3] + w[3] * cp4[3]);
+ }
+ else {
+ cp[0] = (int)(w[0] * cp1[0] + w[1] * cp2[0] + w[2] * cp3[0]);
+ cp[1] = (int)(w[0] * cp1[1] + w[1] * cp2[1] + w[2] * cp3[1]);
+ cp[2] = (int)(w[0] * cp1[2] + w[1] * cp2[2] + w[2] * cp3[2]);
+ cp[3] = (int)(w[0] * cp1[3] + w[1] * cp2[3] + w[2] * cp3[3]);
+ }
+}
+
+static float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, const float fw[4], const float *values)
+{
+ if (values == 0 || index == -1)
+ return 0.0;
+
+ switch (from) {
+ case PART_FROM_VERT:
+ return values[index];
+ case PART_FROM_FACE:
+ case PART_FROM_VOLUME:
+ {
+ MFace *mf = dm->getTessFaceData(dm, index, CD_MFACE);
+ return interpolate_particle_value(values[mf->v1], values[mf->v2], values[mf->v3], values[mf->v4], fw, mf->v4);
+ }
+
+ }
+ return 0.0f;
+}
+
+/* conversion of pa->fw to origspace layer coordinates */
+static void psys_w_to_origspace(const float w[4], float uv[2])
+{
+ uv[0] = w[1] + w[2];
+ uv[1] = w[2] + w[3];
+}
+
+/* conversion of pa->fw to weights in face from origspace */
+static void psys_origspace_to_w(OrigSpaceFace *osface, int quad, const float w[4], float neww[4])
+{
+ float v[4][3], co[3];
+
+ v[0][0] = osface->uv[0][0]; v[0][1] = osface->uv[0][1]; v[0][2] = 0.0f;
+ v[1][0] = osface->uv[1][0]; v[1][1] = osface->uv[1][1]; v[1][2] = 0.0f;
+ v[2][0] = osface->uv[2][0]; v[2][1] = osface->uv[2][1]; v[2][2] = 0.0f;
+
+ psys_w_to_origspace(w, co);
+ co[2] = 0.0f;
+
+ if (quad) {
+ v[3][0] = osface->uv[3][0]; v[3][1] = osface->uv[3][1]; v[3][2] = 0.0f;
+ interp_weights_poly_v3(neww, v, 4, co);
+ }
+ else {
+ interp_weights_poly_v3(neww, v, 3, co);
+ neww[3] = 0.0f;
+ }
+}
+
+/**
+ * Find the final derived mesh tessface for a particle, from its original tessface index.
+ * This is slow and can be optimized but only for many lookups.
+ *
+ * \param dm_final final DM, it may not have the same topology as original mesh.
+ * \param dm_deformed deformed-only DM, it has the exact same topology as original mesh.
+ * \param findex_orig the input tessface index.
+ * \param fw face weights (position of the particle inside the \a findex_orig tessface).
+ * \param poly_nodes may be NULL, otherwise an array of linked list, one for each final DM polygon, containing all
+ * its tessfaces indices.
+ * \return the DM tessface index.
+ */
+int psys_particle_dm_face_lookup(
+ DerivedMesh *dm_final, DerivedMesh *dm_deformed,
+ int findex_orig, const float fw[4], struct LinkNode **poly_nodes)
+{
+ MFace *mtessface_final;
+ OrigSpaceFace *osface_final;
+ int pindex_orig;
+ float uv[2], (*faceuv)[2];
+
+ const int *index_mf_to_mpoly_deformed = NULL;
+ const int *index_mf_to_mpoly = NULL;
+ const int *index_mp_to_orig = NULL;
+
+ const int totface_final = dm_final->getNumTessFaces(dm_final);
+ const int totface_deformed = dm_deformed ? dm_deformed->getNumTessFaces(dm_deformed) : totface_final;
+
+ if (ELEM(0, totface_final, totface_deformed)) {
+ return DMCACHE_NOTFOUND;
+ }
+
+ index_mf_to_mpoly = dm_final->getTessFaceDataArray(dm_final, CD_ORIGINDEX);
+ index_mp_to_orig = dm_final->getPolyDataArray(dm_final, CD_ORIGINDEX);
+ BLI_assert(index_mf_to_mpoly);
+
+ if (dm_deformed) {
+ index_mf_to_mpoly_deformed = dm_deformed->getTessFaceDataArray(dm_deformed, CD_ORIGINDEX);
+ }
+ else {
+ BLI_assert(dm_final->deformedOnly);
+ index_mf_to_mpoly_deformed = index_mf_to_mpoly;
+ }
+ BLI_assert(index_mf_to_mpoly_deformed);
+
+ pindex_orig = index_mf_to_mpoly_deformed[findex_orig];
+
+ if (dm_deformed == NULL) {
+ dm_deformed = dm_final;
+ }
+
+ index_mf_to_mpoly_deformed = NULL;
+
+ mtessface_final = dm_final->getTessFaceArray(dm_final);
+ osface_final = dm_final->getTessFaceDataArray(dm_final, CD_ORIGSPACE);
+
+ if (osface_final == NULL) {
+ /* Assume we don't need osface_final data, and we get a direct 1-1 mapping... */
+ if (findex_orig < totface_final) {
+ //printf("\tNO CD_ORIGSPACE, assuming not needed\n");
+ return findex_orig;
+ }
+ else {
+ printf("\tNO CD_ORIGSPACE, error out of range\n");
+ return DMCACHE_NOTFOUND;
+ }
+ }
+ else if (findex_orig >= dm_deformed->getNumTessFaces(dm_deformed)) {
+ return DMCACHE_NOTFOUND; /* index not in the original mesh */
+ }
+
+ psys_w_to_origspace(fw, uv);
+
+ if (poly_nodes) {
+ /* we can have a restricted linked list of faces to check, faster! */
+ LinkNode *tessface_node = poly_nodes[pindex_orig];
+
+ for (; tessface_node; tessface_node = tessface_node->next) {
+ int findex_dst = GET_INT_FROM_POINTER(tessface_node->link);
+ faceuv = osface_final[findex_dst].uv;
+
+ /* check that this intersects - Its possible this misses :/ -
+ * could also check its not between */
+ if (mtessface_final[findex_dst].v4) {
+ if (isect_point_quad_v2(uv, faceuv[0], faceuv[1], faceuv[2], faceuv[3])) {
+ return findex_dst;
+ }
+ }
+ else if (isect_point_tri_v2(uv, faceuv[0], faceuv[1], faceuv[2])) {
+ return findex_dst;
+ }
+ }
+ }
+ else { /* if we have no node, try every face */
+ for (int findex_dst = 0; findex_dst < totface_final; findex_dst++) {
+ /* If current tessface from 'final' DM and orig tessface (given by index) map to the same orig poly... */
+ if (DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, findex_dst) == pindex_orig) {
+ faceuv = osface_final[findex_dst].uv;
+
+ /* check that this intersects - Its possible this misses :/ -
+ * could also check its not between */
+ if (mtessface_final[findex_dst].v4) {
+ if (isect_point_quad_v2(uv, faceuv[0], faceuv[1], faceuv[2], faceuv[3])) {
+ return findex_dst;
+ }
+ }
+ else if (isect_point_tri_v2(uv, faceuv[0], faceuv[1], faceuv[2])) {
+ return findex_dst;
+ }
+ }
+ }
+ }
+
+ return DMCACHE_NOTFOUND;
+}
+
+static int psys_map_index_on_dm(DerivedMesh *dm, int from, int index, int index_dmcache, const float fw[4], float UNUSED(foffset), int *mapindex, float mapfw[4])
+{
+ if (index < 0)
+ return 0;
+
+ if (dm->deformedOnly || index_dmcache == DMCACHE_ISCHILD) {
+ /* for meshes that are either only deformed or for child particles, the
+ * index and fw do not require any mapping, so we can directly use it */
+ if (from == PART_FROM_VERT) {
+ if (index >= dm->getNumVerts(dm))
+ return 0;
+
+ *mapindex = index;
+ }
+ else { /* FROM_FACE/FROM_VOLUME */
+ if (index >= dm->getNumTessFaces(dm))
+ return 0;
+
+ *mapindex = index;
+ copy_v4_v4(mapfw, fw);
+ }
+ }
+ else {
+ /* for other meshes that have been modified, we try to map the particle
+ * to their new location, which means a different index, and for faces
+ * also a new face interpolation weights */
+ if (from == PART_FROM_VERT) {
+ if (index_dmcache == DMCACHE_NOTFOUND || index_dmcache > dm->getNumVerts(dm))
+ return 0;
+
+ *mapindex = index_dmcache;
+ }
+ else { /* FROM_FACE/FROM_VOLUME */
+ /* find a face on the derived mesh that uses this face */
+ MFace *mface;
+ OrigSpaceFace *osface;
+ int i;
+
+ i = index_dmcache;
+
+ if (i == DMCACHE_NOTFOUND || i >= dm->getNumTessFaces(dm))
+ return 0;
+
+ *mapindex = i;
+
+ /* modify the original weights to become
+ * weights for the derived mesh face */
+ osface = dm->getTessFaceDataArray(dm, CD_ORIGSPACE);
+ mface = dm->getTessFaceData(dm, i, CD_MFACE);
+
+ if (osface == NULL)
+ mapfw[0] = mapfw[1] = mapfw[2] = mapfw[3] = 0.0f;
+ else
+ psys_origspace_to_w(&osface[i], mface->v4, fw, mapfw);
+ }
+ }
+
+ return 1;
+}
+
+/* interprets particle data to get a point on a mesh in object space */
+void psys_particle_on_dm(DerivedMesh *dm_final, int from, int index, int index_dmcache,
+ const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
+ float orco[3], float ornor[3])
+{
+ float tmpnor[3], mapfw[4];
+ float (*orcodata)[3];
+ int mapindex;
+
+ if (!psys_map_index_on_dm(dm_final, from, index, index_dmcache, fw, foffset, &mapindex, mapfw)) {
+ if (vec) { vec[0] = vec[1] = vec[2] = 0.0; }
+ if (nor) { nor[0] = nor[1] = 0.0; nor[2] = 1.0; }
+ if (orco) { orco[0] = orco[1] = orco[2] = 0.0; }
+ if (ornor) { ornor[0] = ornor[1] = 0.0; ornor[2] = 1.0; }
+ if (utan) { utan[0] = utan[1] = utan[2] = 0.0; }
+ if (vtan) { vtan[0] = vtan[1] = vtan[2] = 0.0; }
+
+ return;
+ }
+
+ orcodata = dm_final->getVertDataArray(dm_final, CD_ORCO);
+
+ if (from == PART_FROM_VERT) {
+ dm_final->getVertCo(dm_final, mapindex, vec);
+
+ if (nor) {
+ dm_final->getVertNo(dm_final, mapindex, nor);
+ normalize_v3(nor);
+ }
+
+ if (orco) {
+ if (orcodata) {
+ copy_v3_v3(orco, orcodata[mapindex]);
+ }
+ else {
+ copy_v3_v3(orco, vec);
+ }
+ }
+
+ if (ornor) {
+ dm_final->getVertNo(dm_final, mapindex, ornor);
+ normalize_v3(ornor);
+ }
+
+ if (utan && vtan) {
+ utan[0] = utan[1] = utan[2] = 0.0f;
+ vtan[0] = vtan[1] = vtan[2] = 0.0f;
+ }
+ }
+ else { /* PART_FROM_FACE / PART_FROM_VOLUME */
+ MFace *mface;
+ MTFace *mtface;
+ MVert *mvert;
+
+ mface = dm_final->getTessFaceData(dm_final, mapindex, CD_MFACE);
+ mvert = dm_final->getVertDataArray(dm_final, CD_MVERT);
+ mtface = CustomData_get_layer(&dm_final->faceData, CD_MTFACE);
+
+ if (mtface)
+ mtface += mapindex;
+
+ if (from == PART_FROM_VOLUME) {
+ psys_interpolate_face(mvert, mface, mtface, orcodata, mapfw, vec, tmpnor, utan, vtan, orco, ornor);
+ if (nor)
+ copy_v3_v3(nor, tmpnor);
+
+ normalize_v3(tmpnor); /* XXX Why not normalize tmpnor before copying it into nor??? -- mont29 */
+ mul_v3_fl(tmpnor, -foffset);
+ add_v3_v3(vec, tmpnor);
+ }
+ else
+ psys_interpolate_face(mvert, mface, mtface, orcodata, mapfw, vec, nor, utan, vtan, orco, ornor);
+ }
+}
+
+float psys_particle_value_from_verts(DerivedMesh *dm, short from, ParticleData *pa, float *values)
+{
+ float mapfw[4];
+ int mapindex;
+
+ if (!psys_map_index_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, &mapindex, mapfw))
+ return 0.0f;
+
+ return psys_interpolate_value_from_verts(dm, from, mapindex, mapfw, values);
+}
+
+ParticleSystemModifierData *psys_get_modifier(Object *ob, ParticleSystem *psys)
+{
+ ModifierData *md;
+ ParticleSystemModifierData *psmd;
+
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_ParticleSystem) {
+ psmd = (ParticleSystemModifierData *) md;
+ if (psmd->psys == psys) {
+ return psmd;
+ }
+ }
+ }
+ return NULL;
+}
+/************************************************/
+/* Particles on a shape */
+/************************************************/
+/* ready for future use */
+static void psys_particle_on_shape(int UNUSED(distr), int UNUSED(index),
+ float *UNUSED(fuv), float vec[3], float nor[3], float utan[3], float vtan[3],
+ float orco[3], float ornor[3])
+{
+ /* TODO */
+ float zerovec[3] = {0.0f, 0.0f, 0.0f};
+ if (vec) {
+ copy_v3_v3(vec, zerovec);
+ }
+ if (nor) {
+ copy_v3_v3(nor, zerovec);
+ }
+ if (utan) {
+ copy_v3_v3(utan, zerovec);
+ }
+ if (vtan) {
+ copy_v3_v3(vtan, zerovec);
+ }
+ if (orco) {
+ copy_v3_v3(orco, zerovec);
+ }
+ if (ornor) {
+ copy_v3_v3(ornor, zerovec);
+ }
+}
+/************************************************/
+/* Particles on emitter */
+/************************************************/
+
+CustomDataMask psys_emitter_customdata_mask(ParticleSystem *psys)
+{
+ CustomDataMask dataMask = 0;
+ MTex *mtex;
+ int i;
+
+ if (!psys->part)
+ return 0;
+
+ for (i = 0; i < MAX_MTEX; i++) {
+ mtex = psys->part->mtex[i];
+ if (mtex && mtex->mapto && (mtex->texco & TEXCO_UV))
+ dataMask |= CD_MASK_MTFACE;
+ }
+
+ if (psys->part->tanfac != 0.0f)
+ dataMask |= CD_MASK_MTFACE;
+
+ /* ask for vertexgroups if we need them */
+ for (i = 0; i < PSYS_TOT_VG; i++) {
+ if (psys->vgroup[i]) {
+ dataMask |= CD_MASK_MDEFORMVERT;
+ break;
+ }
+ }
+
+ /* particles only need this if they are after a non deform modifier, and
+ * the modifier stack will only create them in that case. */
+ dataMask |= CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORIGINDEX;
+
+ dataMask |= CD_MASK_ORCO;
+
+ return dataMask;
+}
+
+void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int index, int index_dmcache,
+ float fuv[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
+ float orco[3], float ornor[3])
+{
+ if (psmd && psmd->dm_final) {
+ if (psmd->psys->part->distr == PART_DISTR_GRID && psmd->psys->part->from != PART_FROM_VERT) {
+ if (vec)
+ copy_v3_v3(vec, fuv);
+
+ if (orco)
+ copy_v3_v3(orco, fuv);
+ return;
+ }
+ /* we cant use the num_dmcache */
+ psys_particle_on_dm(psmd->dm_final, from, index, index_dmcache, fuv, foffset, vec, nor, utan, vtan, orco, ornor);
+ }
+ else
+ psys_particle_on_shape(from, index, fuv, vec, nor, utan, vtan, orco, ornor);
+
+}
+/************************************************/
+/* Path Cache */
+/************************************************/
+
+extern void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat,
+ short type, short axis, float obmat[4][4], int smooth_start);
+extern float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
+ bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
+
+void precalc_guides(ParticleSimulationData *sim, ListBase *effectors)
+{
+ EffectedPoint point;
+ ParticleKey state;
+ EffectorData efd;
+ EffectorCache *eff;
+ ParticleSystem *psys = sim->psys;
+ EffectorWeights *weights = sim->psys->part->effector_weights;
+ GuideEffectorData *data;
+ PARTICLE_P;
+
+ if (!effectors)
+ return;
+
+ LOOP_PARTICLES {
+ psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, state.co, 0, 0, 0, 0, 0);
+
+ mul_m4_v3(sim->ob->obmat, state.co);
+ mul_mat3_m4_v3(sim->ob->obmat, state.vel);
+
+ pd_point_from_particle(sim, pa, &state, &point);
+
+ for (eff = effectors->first; eff; eff = eff->next) {
+ if (eff->pd->forcefield != PFIELD_GUIDE)
+ continue;
+
+ if (!eff->guide_data)
+ eff->guide_data = MEM_callocN(sizeof(GuideEffectorData) * psys->totpart, "GuideEffectorData");
+
+ data = eff->guide_data + p;
+
+ sub_v3_v3v3(efd.vec_to_point, state.co, eff->guide_loc);
+ copy_v3_v3(efd.nor, eff->guide_dir);
+ efd.distance = len_v3(efd.vec_to_point);
+
+ copy_v3_v3(data->vec_to_point, efd.vec_to_point);
+ data->strength = effector_falloff(eff, &efd, &point, weights);
+ }
+ }
+}
+
+int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, int index, float time)
+{
+ CurveMapping *clumpcurve = (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) ? part->clumpcurve : NULL;
+ CurveMapping *roughcurve = (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) ? part->roughcurve : NULL;
+ EffectorCache *eff;
+ PartDeflect *pd;
+ Curve *cu;
+ GuideEffectorData *data;
+
+ float effect[3] = {0.0f, 0.0f, 0.0f}, veffect[3] = {0.0f, 0.0f, 0.0f};
+ float guidevec[4], guidedir[3], rot2[4], temp[3];
+ float guidetime, radius, weight, angle, totstrength = 0.0f;
+ float vec_to_point[3];
+
+ if (effectors) for (eff = effectors->first; eff; eff = eff->next) {
+ pd = eff->pd;
+
+ if (pd->forcefield != PFIELD_GUIDE)
+ continue;
+
+ data = eff->guide_data + index;
+
+ if (data->strength <= 0.0f)
+ continue;
+
+ guidetime = time / (1.0f - pd->free_end);
+
+ if (guidetime > 1.0f)
+ continue;
+
+ cu = (Curve *)eff->ob->data;
+
+ if (pd->flag & PFIELD_GUIDE_PATH_ADD) {
+ if (where_on_path(eff->ob, data->strength * guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0)
+ return 0;
+ }
+ else {
+ if (where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0)
+ return 0;
+ }
+
+ mul_m4_v3(eff->ob->obmat, guidevec);
+ mul_mat3_m4_v3(eff->ob->obmat, guidedir);
+
+ normalize_v3(guidedir);
+
+ copy_v3_v3(vec_to_point, data->vec_to_point);
+
+ if (guidetime != 0.0f) {
+ /* curve direction */
+ cross_v3_v3v3(temp, eff->guide_dir, guidedir);
+ angle = dot_v3v3(eff->guide_dir, guidedir) / (len_v3(eff->guide_dir));
+ angle = saacos(angle);
+ axis_angle_to_quat(rot2, temp, angle);
+ mul_qt_v3(rot2, vec_to_point);
+
+ /* curve tilt */
+ axis_angle_to_quat(rot2, guidedir, guidevec[3] - eff->guide_loc[3]);
+ mul_qt_v3(rot2, vec_to_point);
+ }
+
+ /* curve taper */
+ if (cu->taperobj)
+ mul_v3_fl(vec_to_point, BKE_displist_calc_taper(eff->scene, cu->taperobj, (int)(data->strength * guidetime * 100.0f), 100));
+
+ else { /* curve size*/
+ if (cu->flag & CU_PATH_RADIUS) {
+ mul_v3_fl(vec_to_point, radius);
+ }
+ }
+
+ if (clumpcurve)
+ curvemapping_changed_all(clumpcurve);
+ if (roughcurve)
+ curvemapping_changed_all(roughcurve);
+
+ {
+ ParticleKey key;
+ float par_co[3] = {0.0f, 0.0f, 0.0f};
+ float par_vel[3] = {0.0f, 0.0f, 0.0f};
+ float par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f};
+ float orco_offset[3] = {0.0f, 0.0f, 0.0f};
+
+ copy_v3_v3(key.co, vec_to_point);
+ do_kink(&key, par_co, par_vel, par_rot, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0);
+ do_clump(&key, par_co, guidetime, orco_offset, pd->clump_fac, pd->clump_pow, 1.0f,
+ part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve);
+ copy_v3_v3(vec_to_point, key.co);
+ }
+
+ add_v3_v3(vec_to_point, guidevec);
+
+ //sub_v3_v3v3(pa_loc, pa_loc, pa_zero);
+ madd_v3_v3fl(effect, vec_to_point, data->strength);
+ madd_v3_v3fl(veffect, guidedir, data->strength);
+ totstrength += data->strength;
+
+ if (pd->flag & PFIELD_GUIDE_PATH_WEIGHT)
+ totstrength *= weight;
+ }
+
+ if (totstrength != 0.0f) {
+ if (totstrength > 1.0f)
+ mul_v3_fl(effect, 1.0f / totstrength);
+ CLAMP(totstrength, 0.0f, 1.0f);
+ //add_v3_v3(effect, pa_zero);
+ interp_v3_v3v3(state->co, state->co, effect, totstrength);
+
+ normalize_v3(veffect);
+ mul_v3_fl(veffect, len_v3(state->vel));
+ copy_v3_v3(state->vel, veffect);
+ return 1;
+ }
+ return 0;
+}
+
+static void do_path_effectors(ParticleSimulationData *sim, int i, ParticleCacheKey *ca, int k, int steps, float *UNUSED(rootco), float effector, float UNUSED(dfra), float UNUSED(cfra), float *length, float *vec)
+{
+ float force[3] = {0.0f, 0.0f, 0.0f};
+ ParticleKey eff_key;
+ EffectedPoint epoint;
+
+ /* Don't apply effectors for dynamic hair, otherwise the effectors don't get applied twice. */
+ if (sim->psys->flag & PSYS_HAIR_DYNAMICS)
+ return;
+
+ copy_v3_v3(eff_key.co, (ca - 1)->co);
+ copy_v3_v3(eff_key.vel, (ca - 1)->vel);
+ copy_qt_qt(eff_key.rot, (ca - 1)->rot);
+
+ pd_point_from_particle(sim, sim->psys->particles + i, &eff_key, &epoint);
+ pdDoEffectors(sim->psys->effectors, sim->colliders, sim->psys->part->effector_weights, &epoint, force, NULL);
+
+ mul_v3_fl(force, effector * powf((float)k / (float)steps, 100.0f * sim->psys->part->eff_hair) / (float)steps);
+
+ add_v3_v3(force, vec);
+
+ normalize_v3(force);
+
+ if (k < steps)
+ sub_v3_v3v3(vec, (ca + 1)->co, ca->co);
+
+ madd_v3_v3v3fl(ca->co, (ca - 1)->co, force, *length);
+
+ if (k < steps)
+ *length = len_v3(vec);
+}
+static void offset_child(ChildParticle *cpa, ParticleKey *par, float *par_rot, ParticleKey *child, float flat, float radius)
+{
+ copy_v3_v3(child->co, cpa->fuv);
+ mul_v3_fl(child->co, radius);
+
+ child->co[0] *= flat;
+
+ copy_v3_v3(child->vel, par->vel);
+
+ if (par_rot) {
+ mul_qt_v3(par_rot, child->co);
+ copy_qt_qt(child->rot, par_rot);
+ }
+ else
+ unit_qt(child->rot);
+
+ add_v3_v3(child->co, par->co);
+}
+float *psys_cache_vgroup(DerivedMesh *dm, ParticleSystem *psys, int vgroup)
+{
+ float *vg = 0;
+
+ if (vgroup < 0) {
+ /* hair dynamics pinning vgroup */
+
+ }
+ else if (psys->vgroup[vgroup]) {
+ MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
+ if (dvert) {
+ int totvert = dm->getNumVerts(dm), i;
+ vg = MEM_callocN(sizeof(float) * totvert, "vg_cache");
+ if (psys->vg_neg & (1 << vgroup)) {
+ for (i = 0; i < totvert; i++)
+ vg[i] = 1.0f - defvert_find_weight(&dvert[i], psys->vgroup[vgroup] - 1);
+ }
+ else {
+ for (i = 0; i < totvert; i++)
+ vg[i] = defvert_find_weight(&dvert[i], psys->vgroup[vgroup] - 1);
+ }
+ }
+ }
+ return vg;
+}
+void psys_find_parents(ParticleSimulationData *sim, const bool use_render_params)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = sim->psys->part;
+ KDTree *tree;
+ ChildParticle *cpa;
+ ParticleTexture ptex;
+ int p, totparent, totchild = sim->psys->totchild;
+ float co[3], orco[3];
+ int from = PART_FROM_FACE;
+ totparent = (int)(totchild * part->parents * 0.3f);
+
+ if ((sim->psys->renderdata || use_render_params) && part->child_nbr && part->ren_child_nbr)
+ totparent *= (float)part->child_nbr / (float)part->ren_child_nbr;
+
+ /* hard limit, workaround for it being ignored above */
+ if (sim->psys->totpart < totparent) {
+ totparent = sim->psys->totpart;
+ }
+
+ tree = BLI_kdtree_new(totparent);
+
+ for (p = 0, cpa = sim->psys->child; p < totparent; p++, cpa++) {
+ psys_particle_on_emitter(sim->psmd, from, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, 0, 0, 0, orco, 0);
+
+ /* Check if particle doesn't exist because of texture influence. Insert only existing particles into kdtree. */
+ get_cpa_texture(sim->psmd->dm_final, psys, part, psys->particles + cpa->pa[0], p, cpa->num, cpa->fuv, orco, &ptex, PAMAP_DENS | PAMAP_CHILD, psys->cfra);
+
+ if (ptex.exist >= psys_frand(psys, p + 24)) {
+ BLI_kdtree_insert(tree, p, orco);
+ }
+ }
+
+ BLI_kdtree_balance(tree);
+
+ for (; p < totchild; p++, cpa++) {
+ psys_particle_on_emitter(sim->psmd, from, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, 0, 0, 0, orco, 0);
+ cpa->parent = BLI_kdtree_find_nearest(tree, orco, NULL);
+ }
+
+ BLI_kdtree_free(tree);
+}
+
+static bool psys_thread_context_init_path(
+ ParticleThreadContext *ctx, ParticleSimulationData *sim, Scene *scene,
+ float cfra, const bool editupdate, const bool use_render_params)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ int totparent = 0, between = 0;
+ int segments = 1 << part->draw_step;
+ int totchild = psys->totchild;
+
+ psys_thread_context_init(ctx, sim);
+
+ /*---start figuring out what is actually wanted---*/
+ if (psys_in_edit_mode(scene, psys)) {
+ ParticleEditSettings *pset = &scene->toolsettings->particle;
+
+ if ((psys->renderdata == 0 && use_render_params == 0) && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0)
+ totchild = 0;
+
+ segments = 1 << pset->draw_step;
+ }
+
+ if (totchild && part->childtype == PART_CHILD_FACES) {
+ totparent = (int)(totchild * part->parents * 0.3f);
+
+ if ((psys->renderdata || use_render_params) && part->child_nbr && part->ren_child_nbr)
+ totparent *= (float)part->child_nbr / (float)part->ren_child_nbr;
+
+ /* part->parents could still be 0 so we can't test with totparent */
+ between = 1;
+ }
+
+ if (psys->renderdata || use_render_params)
+ segments = 1 << part->ren_step;
+ else {
+ totchild = (int)((float)totchild * (float)part->disp / 100.0f);
+ totparent = MIN2(totparent, totchild);
+ }
+
+ if (totchild == 0)
+ return false;
+
+ /* fill context values */
+ ctx->between = between;
+ ctx->segments = segments;
+ if (ELEM(part->kink, PART_KINK_SPIRAL))
+ ctx->extra_segments = max_ii(part->kink_extra_steps, 1);
+ else
+ ctx->extra_segments = 0;
+ ctx->totchild = totchild;
+ ctx->totparent = totparent;
+ ctx->parent_pass = 0;
+ ctx->cfra = cfra;
+ ctx->editupdate = editupdate;
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&ctx->sim);
+
+ /* cache all relevant vertex groups if they exist */
+ ctx->vg_length = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_LENGTH);
+ ctx->vg_clump = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_CLUMP);
+ ctx->vg_kink = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_KINK);
+ ctx->vg_rough1 = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_ROUGH1);
+ ctx->vg_rough2 = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_ROUGH2);
+ ctx->vg_roughe = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_ROUGHE);
+ if (psys->part->flag & PART_CHILD_EFFECT)
+ ctx->vg_effector = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_EFFECTOR);
+
+ /* prepare curvemapping tables */
+ if ((part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && part->clumpcurve) {
+ ctx->clumpcurve = curvemapping_copy(part->clumpcurve);
+ curvemapping_changed_all(ctx->clumpcurve);
+ }
+ else {
+ ctx->clumpcurve = NULL;
+ }
+ if ((part->child_flag & PART_CHILD_USE_ROUGH_CURVE) && part->roughcurve) {
+ ctx->roughcurve = curvemapping_copy(part->roughcurve);
+ curvemapping_changed_all(ctx->roughcurve);
+ }
+ else {
+ ctx->roughcurve = NULL;
+ }
+
+ return true;
+}
+
+static void psys_task_init_path(ParticleTask *task, ParticleSimulationData *sim)
+{
+ /* init random number generator */
+ int seed = 31415926 + sim->psys->seed;
+
+ task->rng_path = BLI_rng_new(seed);
+}
+
+/* note: this function must be thread safe, except for branching! */
+static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i)
+{
+ ParticleThreadContext *ctx = task->ctx;
+ Object *ob = ctx->sim.ob;
+ ParticleSystem *psys = ctx->sim.psys;
+ ParticleSettings *part = psys->part;
+ ParticleCacheKey **cache = psys->childcache;
+ ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache;
+ ParticleCacheKey *child, *key[4];
+ ParticleTexture ptex;
+ float *cpa_fuv = 0, *par_rot = 0, rot[4];
+ float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3];
+ float eff_length, eff_vec[3], weight[4];
+ int k, cpa_num;
+ short cpa_from;
+
+ if (!pcache)
+ return;
+
+ if (ctx->between) {
+ ParticleData *pa = psys->particles + cpa->pa[0];
+ int w, needupdate;
+ float foffset, wsum = 0.f;
+ float co[3];
+ float p_min = part->parting_min;
+ float p_max = part->parting_max;
+ /* Virtual parents don't work nicely with parting. */
+ float p_fac = part->parents > 0.f ? 0.f : part->parting_fac;
+
+ if (ctx->editupdate) {
+ needupdate = 0;
+ w = 0;
+ while (w < 4 && cpa->pa[w] >= 0) {
+ if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) {
+ needupdate = 1;
+ break;
+ }
+ w++;
+ }
+
+ if (!needupdate)
+ return;
+ else
+ memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
+ }
+
+ /* get parent paths */
+ for (w = 0; w < 4; w++) {
+ if (cpa->pa[w] >= 0) {
+ key[w] = pcache[cpa->pa[w]];
+ weight[w] = cpa->w[w];
+ }
+ else {
+ key[w] = pcache[0];
+ weight[w] = 0.f;
+ }
+ }
+
+ /* modify weights to create parting */
+ if (p_fac > 0.f) {
+ const ParticleCacheKey *key_0_last = pcache_key_segment_endpoint_safe(key[0]);
+ for (w = 0; w < 4; w++) {
+ if (w && (weight[w] > 0.f)) {
+ const ParticleCacheKey *key_w_last = pcache_key_segment_endpoint_safe(key[w]);
+ float d;
+ if (part->flag & PART_CHILD_LONG_HAIR) {
+ /* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */
+ float d1 = len_v3v3(key[0]->co, key[w]->co);
+ float d2 = len_v3v3(key_0_last->co, key_w_last->co);
+
+ d = d1 > 0.f ? d2 / d1 - 1.f : 10000.f;
+ }
+ else {
+ float v1[3], v2[3];
+ sub_v3_v3v3(v1, key_0_last->co, key[0]->co);
+ sub_v3_v3v3(v2, key_w_last->co, key[w]->co);
+ normalize_v3(v1);
+ normalize_v3(v2);
+
+ d = RAD2DEGF(saacos(dot_v3v3(v1, v2)));
+ }
+
+ if (p_max > p_min)
+ d = (d - p_min) / (p_max - p_min);
+ else
+ d = (d - p_min) <= 0.f ? 0.f : 1.f;
+
+ CLAMP(d, 0.f, 1.f);
+
+ if (d > 0.f)
+ weight[w] *= (1.f - d);
+ }
+ wsum += weight[w];
+ }
+ for (w = 0; w < 4; w++)
+ weight[w] /= wsum;
+
+ interp_v4_v4v4(weight, cpa->w, weight, p_fac);
+ }
+
+ /* get the original coordinates (orco) for texture usage */
+ cpa_num = cpa->num;
+
+ foffset = cpa->foffset;
+ cpa_fuv = cpa->fuv;
+ cpa_from = PART_FROM_FACE;
+
+ psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa->fuv, foffset, co, ornor, 0, 0, orco, 0);
+
+ mul_m4_v3(ob->obmat, co);
+
+ for (w = 0; w < 4; w++)
+ sub_v3_v3v3(off1[w], co, key[w]->co);
+
+ psys_mat_hair_to_global(ob, ctx->sim.psmd->dm_final, psys->part->from, pa, hairmat);
+ }
+ else {
+ ParticleData *pa = psys->particles + cpa->parent;
+ float co[3];
+ if (ctx->editupdate) {
+ if (!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC))
+ return;
+
+ memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
+ }
+
+ /* get the parent path */
+ key[0] = pcache[cpa->parent];
+
+ /* get the original coordinates (orco) for texture usage */
+ cpa_from = part->from;
+ cpa_num = pa->num;
+ /* XXX hack to avoid messed up particle num and subsequent crash (#40733) */
+ if (cpa_num > ctx->sim.psmd->dm_final->getNumTessFaces(ctx->sim.psmd->dm_final))
+ cpa_num = 0;
+ cpa_fuv = pa->fuv;
+
+ psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa_fuv, pa->foffset, co, ornor, 0, 0, orco, 0);
+
+ psys_mat_hair_to_global(ob, ctx->sim.psmd->dm_final, psys->part->from, pa, hairmat);
+ }
+
+ child_keys->segments = ctx->segments;
+
+ /* get different child parameters from textures & vgroups */
+ get_child_modifier_parameters(part, ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex);
+
+ if (ptex.exist < psys_frand(psys, i + 24)) {
+ child_keys->segments = -1;
+ return;
+ }
+
+ /* create the child path */
+ for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) {
+ if (ctx->between) {
+ int w = 0;
+
+ zero_v3(child->co);
+ zero_v3(child->vel);
+ unit_qt(child->rot);
+
+ for (w = 0; w < 4; w++) {
+ copy_v3_v3(off2[w], off1[w]);
+
+ if (part->flag & PART_CHILD_LONG_HAIR) {
+ /* Use parent rotation (in addition to emission location) to determine child offset. */
+ if (k)
+ mul_qt_v3((key[w] + k)->rot, off2[w]);
+
+ /* Fade the effect of rotation for even lengths in the end */
+ project_v3_v3v3(dvec, off2[w], (key[w] + k)->vel);
+ madd_v3_v3fl(off2[w], dvec, -(float)k / (float)ctx->segments);
+ }
+
+ add_v3_v3(off2[w], (key[w] + k)->co);
+ }
+
+ /* child position is the weighted sum of parent positions */
+ interp_v3_v3v3v3v3(child->co, off2[0], off2[1], off2[2], off2[3], weight);
+ interp_v3_v3v3v3v3(child->vel, (key[0] + k)->vel, (key[1] + k)->vel, (key[2] + k)->vel, (key[3] + k)->vel, weight);
+
+ copy_qt_qt(child->rot, (key[0] + k)->rot);
+ }
+ else {
+ if (k) {
+ mul_qt_qtqt(rot, (key[0] + k)->rot, key[0]->rot);
+ par_rot = rot;
+ }
+ else {
+ par_rot = key[0]->rot;
+ }
+ /* offset the child from the parent position */
+ offset_child(cpa, (ParticleKey *)(key[0] + k), par_rot, (ParticleKey *)child, part->childflat, part->childrad);
+ }
+
+ child->time = (float)k / (float)ctx->segments;
+ }
+
+ /* apply effectors */
+ if (part->flag & PART_CHILD_EFFECT) {
+ for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) {
+ if (k) {
+ do_path_effectors(&ctx->sim, cpa->pa[0], child, k, ctx->segments, child_keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec);
+ }
+ else {
+ sub_v3_v3v3(eff_vec, (child + 1)->co, child->co);
+ eff_length = len_v3(eff_vec);
+ }
+ }
+ }
+
+ {
+ ParticleData *pa = NULL;
+ ParticleCacheKey *par = NULL;
+ float par_co[3];
+ float par_orco[3];
+
+ if (ctx->totparent) {
+ if (i >= ctx->totparent) {
+ pa = &psys->particles[cpa->parent];
+ /* this is now threadsafe, virtual parents are calculated before rest of children */
+ BLI_assert(cpa->parent < psys->totchildcache);
+ par = cache[cpa->parent];
+ }
+ }
+ else if (cpa->parent >= 0) {
+ pa = &psys->particles[cpa->parent];
+ par = pcache[cpa->parent];
+
+ /* If particle is unexisting, try to pick a viable parent from particles used for interpolation. */
+ for (k = 0; k < 4 && pa && (pa->flag & PARS_UNEXIST); k++) {
+ if (cpa->pa[k] >= 0) {
+ pa = &psys->particles[cpa->pa[k]];
+ par = pcache[cpa->pa[k]];
+ }
+ }
+
+ if (pa->flag & PARS_UNEXIST) pa = NULL;
+ }
+
+ if (pa) {
+ ListBase modifiers;
+ BLI_listbase_clear(&modifiers);
+
+ psys_particle_on_emitter(ctx->sim.psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset,
+ par_co, NULL, NULL, NULL, par_orco, NULL);
+
+ psys_apply_child_modifiers(ctx, &modifiers, cpa, &ptex, orco, ornor, hairmat, child_keys, par, par_orco);
+ }
+ else
+ zero_v3(par_orco);
+ }
+
+ /* Hide virtual parents */
+ if (i < ctx->totparent)
+ child_keys->segments = -1;
+}
+
+static void exec_child_path_cache(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid))
+{
+ ParticleTask *task = taskdata;
+ ParticleThreadContext *ctx = task->ctx;
+ ParticleSystem *psys = ctx->sim.psys;
+ ParticleCacheKey **cache = psys->childcache;
+ ChildParticle *cpa;
+ int i;
+
+ cpa = psys->child + task->begin;
+ for (i = task->begin; i < task->end; ++i, ++cpa) {
+ BLI_assert(i < psys->totchildcache);
+ psys_thread_create_path(task, cpa, cache[i], i);
+ }
+}
+
+void psys_cache_child_paths(
+ ParticleSimulationData *sim, float cfra,
+ const bool editupdate, const bool use_render_params)
+{
+ TaskScheduler *task_scheduler;
+ TaskPool *task_pool;
+ ParticleThreadContext ctx;
+ ParticleTask *tasks_parent, *tasks_child;
+ int numtasks_parent, numtasks_child;
+ int i, totchild, totparent;
+
+ if (sim->psys->flag & PSYS_GLOBAL_HAIR)
+ return;
+
+ /* create a task pool for child path tasks */
+ if (!psys_thread_context_init_path(&ctx, sim, sim->scene, cfra, editupdate, use_render_params))
+ return;
+
+ task_scheduler = BLI_task_scheduler_get();
+ task_pool = BLI_task_pool_create(task_scheduler, &ctx);
+ totchild = ctx.totchild;
+ totparent = ctx.totparent;
+
+ if (editupdate && sim->psys->childcache && totchild == sim->psys->totchildcache) {
+ ; /* just overwrite the existing cache */
+ }
+ else {
+ /* clear out old and create new empty path cache */
+ free_child_path_cache(sim->psys);
+
+ sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx.segments + ctx.extra_segments + 1);
+ sim->psys->totchildcache = totchild;
+ }
+
+ /* cache parent paths */
+ ctx.parent_pass = 1;
+ psys_tasks_create(&ctx, 0, totparent, &tasks_parent, &numtasks_parent);
+ for (i = 0; i < numtasks_parent; ++i) {
+ ParticleTask *task = &tasks_parent[i];
+
+ psys_task_init_path(task, sim);
+ BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW);
+ }
+ BLI_task_pool_work_and_wait(task_pool);
+
+ /* cache child paths */
+ ctx.parent_pass = 0;
+ psys_tasks_create(&ctx, totparent, totchild, &tasks_child, &numtasks_child);
+ for (i = 0; i < numtasks_child; ++i) {
+ ParticleTask *task = &tasks_child[i];
+
+ psys_task_init_path(task, sim);
+ BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW);
+ }
+ BLI_task_pool_work_and_wait(task_pool);
+
+ BLI_task_pool_free(task_pool);
+
+ psys_tasks_free(tasks_parent, numtasks_parent);
+ psys_tasks_free(tasks_child, numtasks_child);
+
+ psys_thread_context_free(&ctx);
+}
+
+/* figure out incremental rotations along path starting from unit quat */
+static void cache_key_incremental_rotation(ParticleCacheKey *key0, ParticleCacheKey *key1, ParticleCacheKey *key2, float *prev_tangent, int i)
+{
+ float cosangle, angle, tangent[3], normal[3], q[4];
+
+ switch (i) {
+ case 0:
+ /* start from second key */
+ break;
+ case 1:
+ /* calculate initial tangent for incremental rotations */
+ sub_v3_v3v3(prev_tangent, key0->co, key1->co);
+ normalize_v3(prev_tangent);
+ unit_qt(key1->rot);
+ break;
+ default:
+ sub_v3_v3v3(tangent, key0->co, key1->co);
+ normalize_v3(tangent);
+
+ cosangle = dot_v3v3(tangent, prev_tangent);
+
+ /* note we do the comparison on cosangle instead of
+ * angle, since floating point accuracy makes it give
+ * different results across platforms */
+ if (cosangle > 0.999999f) {
+ copy_v4_v4(key1->rot, key2->rot);
+ }
+ else {
+ angle = saacos(cosangle);
+ cross_v3_v3v3(normal, prev_tangent, tangent);
+ axis_angle_to_quat(q, normal, angle);
+ mul_qt_qtqt(key1->rot, q, key2->rot);
+ }
+
+ copy_v3_v3(prev_tangent, tangent);
+ }
+}
+
+/**
+ * Calculates paths ready for drawing/rendering
+ * - Useful for making use of opengl vertex arrays for super fast strand drawing.
+ * - Makes child strands possible and creates them too into the cache.
+ * - Cached path data is also used to determine cut position for the editmode tool. */
+void psys_cache_paths(ParticleSimulationData *sim, float cfra, const bool use_render_params)
+{
+ PARTICLE_PSMD;
+ ParticleEditSettings *pset = &sim->scene->toolsettings->particle;
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleCacheKey *ca, **cache;
+
+ DerivedMesh *hair_dm = (psys->part->type == PART_HAIR && psys->flag & PSYS_HAIR_DYNAMICS) ? psys->hair_out_dm : NULL;
+
+ ParticleKey result;
+
+ Material *ma;
+ ParticleInterpolationData pind;
+ ParticleTexture ptex;
+
+ PARTICLE_P;
+
+ float birthtime = 0.0, dietime = 0.0;
+ float t, time = 0.0, dfra = 1.0 /* , frs_sec = sim->scene->r.frs_sec*/ /*UNUSED*/;
+ float col[4] = {0.5f, 0.5f, 0.5f, 1.0f};
+ float prev_tangent[3] = {0.0f, 0.0f, 0.0f}, hairmat[4][4];
+ float rotmat[3][3];
+ int k;
+ int segments = (int)pow(2.0, (double)((psys->renderdata || use_render_params) ? part->ren_step : part->draw_step));
+ int totpart = psys->totpart;
+ float length, vec[3];
+ float *vg_effector = NULL;
+ float *vg_length = NULL, pa_length = 1.0f;
+ int keyed, baked;
+
+ /* we don't have anything valid to create paths from so let's quit here */
+ if ((psys->flag & PSYS_HAIR_DONE || psys->flag & PSYS_KEYED || psys->pointcache) == 0)
+ return;
+
+ if (psys_in_edit_mode(sim->scene, psys))
+ if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0)
+ return;
+
+ keyed = psys->flag & PSYS_KEYED;
+ baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR;
+
+ /* clear out old and create new empty path cache */
+ psys_free_path_cache(psys, psys->edit);
+ cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, segments + 1);
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(sim);
+ ma = give_current_material(sim->ob, psys->part->omat);
+ if (ma && (psys->part->draw_col == PART_DRAW_COL_MAT))
+ copy_v3_v3(col, &ma->r);
+
+ if ((psys->flag & PSYS_GLOBAL_HAIR) == 0) {
+ if ((psys->part->flag & PART_CHILD_EFFECT) == 0)
+ vg_effector = psys_cache_vgroup(psmd->dm_final, psys, PSYS_VG_EFFECTOR);
+
+ if (!psys->totchild)
+ vg_length = psys_cache_vgroup(psmd->dm_final, psys, PSYS_VG_LENGTH);
+ }
+
+ /* ensure we have tessfaces to be used for mapping */
+ if (part->from != PART_FROM_VERT) {
+ DM_ensure_tessface(psmd->dm_final);
+ }
+
+ /*---first main loop: create all actual particles' paths---*/
+ LOOP_PARTICLES {
+ if (!psys->totchild) {
+ psys_get_texture(sim, pa, &ptex, PAMAP_LENGTH, 0.f);
+ pa_length = ptex.length * (1.0f - part->randlength * psys_frand(psys, psys->seed + p));
+ if (vg_length)
+ pa_length *= psys_particle_value_from_verts(psmd->dm_final, part->from, pa, vg_length);
+ }
+
+ pind.keyed = keyed;
+ pind.cache = baked ? psys->pointcache : NULL;
+ pind.epoint = NULL;
+ pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE);
+ pind.dm = hair_dm;
+
+ memset(cache[p], 0, sizeof(*cache[p]) * (segments + 1));
+
+ cache[p]->segments = segments;
+
+ /*--get the first data points--*/
+ init_particle_interpolation(sim->ob, sim->psys, pa, &pind);
+
+ /* hairmat is needed for for non-hair particle too so we get proper rotations */
+ psys_mat_hair_to_global(sim->ob, psmd->dm_final, psys->part->from, pa, hairmat);
+ copy_v3_v3(rotmat[0], hairmat[2]);
+ copy_v3_v3(rotmat[1], hairmat[1]);
+ copy_v3_v3(rotmat[2], hairmat[0]);
+
+ if (part->draw & PART_ABS_PATH_TIME) {
+ birthtime = MAX2(pind.birthtime, part->path_start);
+ dietime = MIN2(pind.dietime, part->path_end);
+ }
+ else {
+ float tb = pind.birthtime;
+ birthtime = tb + part->path_start * (pind.dietime - tb);
+ dietime = tb + part->path_end * (pind.dietime - tb);
+ }
+
+ if (birthtime >= dietime) {
+ cache[p]->segments = -1;
+ continue;
+ }
+
+ dietime = birthtime + pa_length * (dietime - birthtime);
+
+ /*--interpolate actual path from data points--*/
+ for (k = 0, ca = cache[p]; k <= segments; k++, ca++) {
+ time = (float)k / (float)segments;
+ t = birthtime + time * (dietime - birthtime);
+ result.time = -t;
+ do_particle_interpolation(psys, p, pa, t, &pind, &result);
+ copy_v3_v3(ca->co, result.co);
+
+ /* dynamic hair is in object space */
+ /* keyed and baked are already in global space */
+ if (hair_dm)
+ mul_m4_v3(sim->ob->obmat, ca->co);
+ else if (!keyed && !baked && !(psys->flag & PSYS_GLOBAL_HAIR))
+ mul_m4_v3(hairmat, ca->co);
+
+ copy_v3_v3(ca->col, col);
+ }
+
+ if (part->type == PART_HAIR) {
+ HairKey *hkey;
+
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ mul_v3_m4v3(hkey->world_co, hairmat, hkey->co);
+ }
+ }
+
+ /*--modify paths and calculate rotation & velocity--*/
+
+ if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
+ /* apply effectors */
+ if ((psys->part->flag & PART_CHILD_EFFECT) == 0) {
+ float effector = 1.0f;
+ if (vg_effector)
+ effector *= psys_particle_value_from_verts(psmd->dm_final, psys->part->from, pa, vg_effector);
+
+ sub_v3_v3v3(vec, (cache[p] + 1)->co, cache[p]->co);
+ length = len_v3(vec);
+
+ for (k = 1, ca = cache[p] + 1; k <= segments; k++, ca++)
+ do_path_effectors(sim, p, ca, k, segments, cache[p]->co, effector, dfra, cfra, &length, vec);
+ }
+
+ /* apply guide curves to path data */
+ if (sim->psys->effectors && (psys->part->flag & PART_CHILD_EFFECT) == 0) {
+ for (k = 0, ca = cache[p]; k <= segments; k++, ca++)
+ /* ca is safe to cast, since only co and vel are used */
+ do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)segments);
+ }
+
+ /* lattices have to be calculated separately to avoid mixups between effector calculations */
+ if (psys->lattice_deform_data) {
+ for (k = 0, ca = cache[p]; k <= segments; k++, ca++)
+ calc_latt_deform(psys->lattice_deform_data, ca->co, 1.0f);
+ }
+ }
+
+ /* finally do rotation & velocity */
+ for (k = 1, ca = cache[p] + 1; k <= segments; k++, ca++) {
+ cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k);
+
+ if (k == segments)
+ copy_qt_qt(ca->rot, (ca - 1)->rot);
+
+ /* set velocity */
+ sub_v3_v3v3(ca->vel, ca->co, (ca - 1)->co);
+
+ if (k == 1)
+ copy_v3_v3((ca - 1)->vel, ca->vel);
+
+ ca->time = (float)k / (float)segments;
+ }
+ /* First rotation is based on emitting face orientation.
+ * This is way better than having flipping rotations resulting
+ * from using a global axis as a rotation pole (vec_to_quat()).
+ * It's not an ideal solution though since it disregards the
+ * initial tangent, but taking that in to account will allow
+ * the possibility of flipping again. -jahka
+ */
+ mat3_to_quat_is_ok(cache[p]->rot, rotmat);
+ }
+
+ psys->totcached = totpart;
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (vg_effector)
+ MEM_freeN(vg_effector);
+
+ if (vg_length)
+ MEM_freeN(vg_length);
+}
+void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cfra, const bool use_render_params)
+{
+ ParticleCacheKey *ca, **cache = edit->pathcache;
+ ParticleEditSettings *pset = &scene->toolsettings->particle;
+
+ PTCacheEditPoint *point = NULL;
+ PTCacheEditKey *ekey = NULL;
+
+ ParticleSystem *psys = edit->psys;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ ParticleData *pa = psys ? psys->particles : NULL;
+
+ ParticleInterpolationData pind;
+ ParticleKey result;
+
+ float birthtime = 0.0f, dietime = 0.0f;
+ float t, time = 0.0f, keytime = 0.0f /*, frs_sec */;
+ float hairmat[4][4], rotmat[3][3], prev_tangent[3] = {0.0f, 0.0f, 0.0f};
+ int k, i;
+ int segments = 1 << pset->draw_step;
+ int totpart = edit->totpoint, recalc_set = 0;
+ float sel_col[3];
+ float nosel_col[3];
+
+ segments = MAX2(segments, 4);
+
+ if (!cache || edit->totpoint != edit->totcached) {
+ /* clear out old and create new empty path cache */
+ psys_free_path_cache(edit->psys, edit);
+ cache = edit->pathcache = psys_alloc_path_cache_buffers(&edit->pathcachebufs, totpart, segments + 1);
+
+ /* set flag for update (child particles check this too) */
+ for (i = 0, point = edit->points; i < totpart; i++, point++)
+ point->flag |= PEP_EDIT_RECALC;
+ recalc_set = 1;
+ }
+
+ /* frs_sec = (psys || edit->pid.flag & PTCACHE_VEL_PER_SEC) ? 25.0f : 1.0f; */ /* UNUSED */
+
+ if (pset->brushtype == PE_BRUSH_WEIGHT) {
+ ; /* use weight painting colors now... */
+ }
+ else {
+ sel_col[0] = (float)edit->sel_col[0] / 255.0f;
+ sel_col[1] = (float)edit->sel_col[1] / 255.0f;
+ sel_col[2] = (float)edit->sel_col[2] / 255.0f;
+ nosel_col[0] = (float)edit->nosel_col[0] / 255.0f;
+ nosel_col[1] = (float)edit->nosel_col[1] / 255.0f;
+ nosel_col[2] = (float)edit->nosel_col[2] / 255.0f;
+ }
+
+ /*---first main loop: create all actual particles' paths---*/
+ for (i = 0, point = edit->points; i < totpart; i++, pa += pa ? 1 : 0, point++) {
+ if (edit->totcached && !(point->flag & PEP_EDIT_RECALC))
+ continue;
+
+ if (point->totkey == 0)
+ continue;
+
+ ekey = point->keys;
+
+ pind.keyed = 0;
+ pind.cache = NULL;
+ pind.epoint = point;
+ pind.bspline = psys ? (psys->part->flag & PART_HAIR_BSPLINE) : 0;
+ pind.dm = NULL;
+
+
+ /* should init_particle_interpolation set this ? */
+ if (pset->brushtype == PE_BRUSH_WEIGHT) {
+ pind.hkey[0] = NULL;
+ /* pa != NULL since the weight brush is only available for hair */
+ pind.hkey[1] = pa->hair;
+ }
+
+
+ memset(cache[i], 0, sizeof(*cache[i]) * (segments + 1));
+
+ cache[i]->segments = segments;
+
+ /*--get the first data points--*/
+ init_particle_interpolation(ob, psys, pa, &pind);
+
+ if (psys) {
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, pa, hairmat);
+ copy_v3_v3(rotmat[0], hairmat[2]);
+ copy_v3_v3(rotmat[1], hairmat[1]);
+ copy_v3_v3(rotmat[2], hairmat[0]);
+ }
+
+ birthtime = pind.birthtime;
+ dietime = pind.dietime;
+
+ if (birthtime >= dietime) {
+ cache[i]->segments = -1;
+ continue;
+ }
+
+ /*--interpolate actual path from data points--*/
+ for (k = 0, ca = cache[i]; k <= segments; k++, ca++) {
+ time = (float)k / (float)segments;
+ t = birthtime + time * (dietime - birthtime);
+ result.time = -t;
+ do_particle_interpolation(psys, i, pa, t, &pind, &result);
+ copy_v3_v3(ca->co, result.co);
+
+ /* non-hair points are already in global space */
+ if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
+ mul_m4_v3(hairmat, ca->co);
+
+ if (k) {
+ cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k);
+
+ if (k == segments)
+ copy_qt_qt(ca->rot, (ca - 1)->rot);
+
+ /* set velocity */
+ sub_v3_v3v3(ca->vel, ca->co, (ca - 1)->co);
+
+ if (k == 1)
+ copy_v3_v3((ca - 1)->vel, ca->vel);
+ }
+ }
+ else {
+ ca->vel[0] = ca->vel[1] = 0.0f;
+ ca->vel[2] = 1.0f;
+ }
+
+ /* selection coloring in edit mode */
+ if (pset->brushtype == PE_BRUSH_WEIGHT) {
+ float t2;
+
+ if (k == 0) {
+ weight_to_rgb(ca->col, pind.hkey[1]->weight);
+ }
+ else {
+ float w1[3], w2[3];
+ keytime = (t - (*pind.ekey[0]->time)) / ((*pind.ekey[1]->time) - (*pind.ekey[0]->time));
+
+ weight_to_rgb(w1, pind.hkey[0]->weight);
+ weight_to_rgb(w2, pind.hkey[1]->weight);
+
+ interp_v3_v3v3(ca->col, w1, w2, keytime);
+ }
+
+ /* at the moment this is only used for weight painting.
+ * will need to move out of this check if its used elsewhere. */
+ t2 = birthtime + ((float)k / (float)segments) * (dietime - birthtime);
+
+ while (pind.hkey[1]->time < t2) pind.hkey[1]++;
+ pind.hkey[0] = pind.hkey[1] - 1;
+ }
+ else {
+ if ((ekey + (pind.ekey[0] - point->keys))->flag & PEK_SELECT) {
+ if ((ekey + (pind.ekey[1] - point->keys))->flag & PEK_SELECT) {
+ copy_v3_v3(ca->col, sel_col);
+ }
+ else {
+ keytime = (t - (*pind.ekey[0]->time)) / ((*pind.ekey[1]->time) - (*pind.ekey[0]->time));
+ interp_v3_v3v3(ca->col, sel_col, nosel_col, keytime);
+ }
+ }
+ else {
+ if ((ekey + (pind.ekey[1] - point->keys))->flag & PEK_SELECT) {
+ keytime = (t - (*pind.ekey[0]->time)) / ((*pind.ekey[1]->time) - (*pind.ekey[0]->time));
+ interp_v3_v3v3(ca->col, nosel_col, sel_col, keytime);
+ }
+ else {
+ copy_v3_v3(ca->col, nosel_col);
+ }
+ }
+ }
+
+ ca->time = t;
+ }
+ if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
+ /* First rotation is based on emitting face orientation.
+ * This is way better than having flipping rotations resulting
+ * from using a global axis as a rotation pole (vec_to_quat()).
+ * It's not an ideal solution though since it disregards the
+ * initial tangent, but taking that in to account will allow
+ * the possibility of flipping again. -jahka
+ */
+ mat3_to_quat_is_ok(cache[i]->rot, rotmat);
+ }
+ }
+
+ edit->totcached = totpart;
+
+ if (psys) {
+ ParticleSimulationData sim = {0};
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(ob, psys);
+
+ psys_cache_child_paths(&sim, cfra, true, use_render_params);
+ }
+
+ /* clear recalc flag if set here */
+ if (recalc_set) {
+ for (i = 0, point = edit->points; i < totpart; i++, point++)
+ point->flag &= ~PEP_EDIT_RECALC;
+ }
+}
+/************************************************/
+/* Particle Key handling */
+/************************************************/
+void copy_particle_key(ParticleKey *to, ParticleKey *from, int time)
+{
+ if (time) {
+ memcpy(to, from, sizeof(ParticleKey));
+ }
+ else {
+ float to_time = to->time;
+ memcpy(to, from, sizeof(ParticleKey));
+ to->time = to_time;
+ }
+}
+void psys_get_from_key(ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time)
+{
+ if (loc) copy_v3_v3(loc, key->co);
+ if (vel) copy_v3_v3(vel, key->vel);
+ if (rot) copy_qt_qt(rot, key->rot);
+ if (time) *time = key->time;
+}
+/*-------changing particle keys from space to another-------*/
+#if 0
+static void key_from_object(Object *ob, ParticleKey *key)
+{
+ float q[4];
+
+ add_v3_v3(key->vel, key->co);
+
+ mul_m4_v3(ob->obmat, key->co);
+ mul_m4_v3(ob->obmat, key->vel);
+ mat4_to_quat(q, ob->obmat);
+
+ sub_v3_v3v3(key->vel, key->vel, key->co);
+ mul_qt_qtqt(key->rot, q, key->rot);
+}
+#endif
+
+static void triatomat(float *v1, float *v2, float *v3, float (*uv)[2], float mat[4][4])
+{
+ float det, w1, w2, d1[2], d2[2];
+
+ memset(mat, 0, sizeof(float) * 4 * 4);
+ mat[3][3] = 1.0f;
+
+ /* first axis is the normal */
+ normal_tri_v3(mat[2], v1, v2, v3);
+
+ /* second axis along (1, 0) in uv space */
+ if (uv) {
+ d1[0] = uv[1][0] - uv[0][0];
+ d1[1] = uv[1][1] - uv[0][1];
+ d2[0] = uv[2][0] - uv[0][0];
+ d2[1] = uv[2][1] - uv[0][1];
+
+ det = d2[0] * d1[1] - d2[1] * d1[0];
+
+ if (det != 0.0f) {
+ det = 1.0f / det;
+ w1 = -d2[1] * det;
+ w2 = d1[1] * det;
+
+ mat[1][0] = w1 * (v2[0] - v1[0]) + w2 * (v3[0] - v1[0]);
+ mat[1][1] = w1 * (v2[1] - v1[1]) + w2 * (v3[1] - v1[1]);
+ mat[1][2] = w1 * (v2[2] - v1[2]) + w2 * (v3[2] - v1[2]);
+ normalize_v3(mat[1]);
+ }
+ else
+ mat[1][0] = mat[1][1] = mat[1][2] = 0.0f;
+ }
+ else {
+ sub_v3_v3v3(mat[1], v2, v1);
+ normalize_v3(mat[1]);
+ }
+
+ /* third as a cross product */
+ cross_v3_v3v3(mat[0], mat[1], mat[2]);
+}
+
+static void psys_face_mat(Object *ob, DerivedMesh *dm, ParticleData *pa, float mat[4][4], int orco)
+{
+ float v[3][3];
+ MFace *mface;
+ OrigSpaceFace *osface;
+ float (*orcodata)[3];
+
+ int i = (ELEM(pa->num_dmcache, DMCACHE_ISCHILD, DMCACHE_NOTFOUND)) ? pa->num : pa->num_dmcache;
+ if (i == -1 || i >= dm->getNumTessFaces(dm)) { unit_m4(mat); return; }
+
+ mface = dm->getTessFaceData(dm, i, CD_MFACE);
+ osface = dm->getTessFaceData(dm, i, CD_ORIGSPACE);
+
+ if (orco && (orcodata = dm->getVertDataArray(dm, CD_ORCO))) {
+ copy_v3_v3(v[0], orcodata[mface->v1]);
+ copy_v3_v3(v[1], orcodata[mface->v2]);
+ copy_v3_v3(v[2], orcodata[mface->v3]);
+
+ /* ugly hack to use non-transformed orcos, since only those
+ * give symmetric results for mirroring in particle mode */
+ if (DM_get_vert_data_layer(dm, CD_ORIGINDEX))
+ BKE_mesh_orco_verts_transform(ob->data, v, 3, 1);
+ }
+ else {
+ dm->getVertCo(dm, mface->v1, v[0]);
+ dm->getVertCo(dm, mface->v2, v[1]);
+ dm->getVertCo(dm, mface->v3, v[2]);
+ }
+
+ triatomat(v[0], v[1], v[2], (osface) ? osface->uv : NULL, mat);
+}
+
+void psys_mat_hair_to_object(Object *UNUSED(ob), DerivedMesh *dm, short from, ParticleData *pa, float hairmat[4][4])
+{
+ float vec[3];
+
+ /* can happen when called from a different object's modifier */
+ if (!dm) {
+ unit_m4(hairmat);
+ return;
+ }
+
+ psys_face_mat(0, dm, pa, hairmat, 0);
+ psys_particle_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, 0, 0);
+ copy_v3_v3(hairmat[3], vec);
+}
+
+void psys_mat_hair_to_orco(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[4][4])
+{
+ float vec[3], orco[3];
+
+ psys_face_mat(ob, dm, pa, hairmat, 1);
+ psys_particle_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, orco, 0);
+
+ /* see psys_face_mat for why this function is called */
+ if (DM_get_vert_data_layer(dm, CD_ORIGINDEX))
+ BKE_mesh_orco_verts_transform(ob->data, &orco, 1, 1);
+ copy_v3_v3(hairmat[3], orco);
+}
+
+void psys_vec_rot_to_face(DerivedMesh *dm, ParticleData *pa, float vec[3])
+{
+ float mat[4][4];
+
+ psys_face_mat(0, dm, pa, mat, 0);
+ transpose_m4(mat); /* cheap inverse for rotation matrix */
+ mul_mat3_m4_v3(mat, vec);
+}
+
+void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[4][4])
+{
+ float facemat[4][4];
+
+ psys_mat_hair_to_object(ob, dm, from, pa, facemat);
+
+ mul_m4_m4m4(hairmat, ob->obmat, facemat);
+}
+
+/************************************************/
+/* ParticleSettings handling */
+/************************************************/
+ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *name)
+{
+ ParticleSystem *psys;
+ ModifierData *md;
+ ParticleSystemModifierData *psmd;
+
+ if (!ob || ob->type != OB_MESH)
+ return NULL;
+
+ psys = ob->particlesystem.first;
+ for (; psys; psys = psys->next)
+ psys->flag &= ~PSYS_CURRENT;
+
+ psys = MEM_callocN(sizeof(ParticleSystem), "particle_system");
+ psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
+ BLI_addtail(&ob->particlesystem, psys);
+
+ psys->part = psys_new_settings(DATA_("ParticleSettings"), NULL);
+
+ if (BLI_listbase_count_ex(&ob->particlesystem, 2) > 1)
+ BLI_snprintf(psys->name, sizeof(psys->name), DATA_("ParticleSystem %i"), BLI_listbase_count(&ob->particlesystem));
+ else
+ BLI_strncpy(psys->name, DATA_("ParticleSystem"), sizeof(psys->name));
+
+ md = modifier_new(eModifierType_ParticleSystem);
+
+ if (name)
+ BLI_strncpy_utf8(md->name, name, sizeof(md->name));
+ else
+ BLI_snprintf(md->name, sizeof(md->name), DATA_("ParticleSystem %i"), BLI_listbase_count(&ob->particlesystem));
+ modifier_unique_name(&ob->modifiers, md);
+
+ psmd = (ParticleSystemModifierData *) md;
+ psmd->psys = psys;
+ BLI_addtail(&ob->modifiers, md);
+
+ psys->totpart = 0;
+ psys->flag = PSYS_CURRENT;
+ psys->cfra = BKE_scene_frame_get_from_ctime(scene, CFRA + 1);
+
+ DAG_relations_tag_update(G.main);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ return md;
+}
+void object_remove_particle_system(Scene *UNUSED(scene), Object *ob)
+{
+ ParticleSystem *psys = psys_get_current(ob);
+ ParticleSystemModifierData *psmd;
+ ModifierData *md;
+
+ if (!psys)
+ return;
+
+ /* clear all other appearances of this pointer (like on smoke flow modifier) */
+ if ((md = modifiers_findByType(ob, eModifierType_Smoke))) {
+ SmokeModifierData *smd = (SmokeModifierData *)md;
+ if ((smd->type == MOD_SMOKE_TYPE_FLOW) && smd->flow && smd->flow->psys)
+ if (smd->flow->psys == psys)
+ smd->flow->psys = NULL;
+ }
+
+ if ((md = modifiers_findByType(ob, eModifierType_DynamicPaint))) {
+ DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
+ if (pmd->brush && pmd->brush->psys)
+ if (pmd->brush->psys == psys)
+ pmd->brush->psys = NULL;
+ }
+
+ /* clear modifier */
+ psmd = psys_get_modifier(ob, psys);
+ BLI_remlink(&ob->modifiers, psmd);
+ modifier_free((ModifierData *)psmd);
+
+ /* clear particle system */
+ BLI_remlink(&ob->particlesystem, psys);
+ if (psys->part) {
+ id_us_min(&psys->part->id);
+ }
+ psys_free(ob, psys);
+
+ if (ob->particlesystem.first)
+ ((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT;
+ else
+ ob->mode &= ~OB_MODE_PARTICLE_EDIT;
+
+ DAG_relations_tag_update(G.main);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+}
+
+static void default_particle_settings(ParticleSettings *part)
+{
+ part->type = PART_EMITTER;
+ part->distr = PART_DISTR_JIT;
+ part->draw_as = PART_DRAW_REND;
+ part->ren_as = PART_DRAW_HALO;
+ part->bb_uv_split = 1;
+ part->bb_align = PART_BB_VIEW;
+ part->bb_split_offset = PART_BB_OFF_LINEAR;
+ part->flag = PART_EDISTR | PART_TRAND | PART_HIDE_ADVANCED_HAIR;
+
+ part->sta = 1.0;
+ part->end = 200.0;
+ part->lifetime = 50.0;
+ part->jitfac = 1.0;
+ part->totpart = 1000;
+ part->grid_res = 10;
+ part->timetweak = 1.0;
+ part->courant_target = 0.2;
+
+ part->integrator = PART_INT_MIDPOINT;
+ part->phystype = PART_PHYS_NEWTON;
+ part->hair_step = 5;
+ part->keys_step = 5;
+ part->draw_step = 2;
+ part->ren_step = 3;
+ part->adapt_angle = 5;
+ part->adapt_pix = 3;
+ part->kink_axis = 2;
+ part->kink_amp_clump = 1.f;
+ part->kink_extra_steps = 4;
+ part->clump_noise_size = 1.0f;
+ part->reactevent = PART_EVENT_DEATH;
+ part->disp = 100;
+ part->from = PART_FROM_FACE;
+
+ part->normfac = 1.0f;
+
+ part->mass = 1.0;
+ part->size = 0.05;
+ part->childsize = 1.0;
+
+ part->rotmode = PART_ROT_VEL;
+ part->avemode = PART_AVE_VELOCITY;
+
+ part->child_nbr = 10;
+ part->ren_child_nbr = 100;
+ part->childrad = 0.2f;
+ part->childflat = 0.0f;
+ part->clumppow = 0.0f;
+ part->kink_amp = 0.2f;
+ part->kink_freq = 2.0;
+
+ part->rough1_size = 1.0;
+ part->rough2_size = 1.0;
+ part->rough_end_shape = 1.0;
+
+ part->clength = 1.0f;
+ part->clength_thres = 0.0f;
+
+ part->draw = PART_DRAW_EMITTER;
+ part->draw_line[0] = 0.5;
+ part->path_start = 0.0f;
+ part->path_end = 1.0f;
+
+ part->bb_size[0] = part->bb_size[1] = 1.0f;
+
+ part->keyed_loops = 1;
+
+ part->color_vec_max = 1.f;
+ part->draw_col = PART_DRAW_COL_MAT;
+
+ part->simplify_refsize = 1920;
+ part->simplify_rate = 1.0f;
+ part->simplify_transition = 0.1f;
+ part->simplify_viewport = 0.8;
+
+ if (!part->effector_weights)
+ part->effector_weights = BKE_add_effector_weights(NULL);
+
+ part->omat = 1;
+ part->use_modifier_stack = false;
+}
+
+
+ParticleSettings *psys_new_settings(const char *name, Main *main)
+{
+ ParticleSettings *part;
+
+ if (main == NULL)
+ main = G.main;
+
+ part = BKE_libblock_alloc(main, ID_PA, name);
+
+ default_particle_settings(part);
+
+ return part;
+}
+
+void BKE_particlesettings_clump_curve_init(ParticleSettings *part)
+{
+ CurveMapping *cumap = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+
+ cumap->cm[0].curve[0].x = 0.0f;
+ cumap->cm[0].curve[0].y = 1.0f;
+ cumap->cm[0].curve[1].x = 1.0f;
+ cumap->cm[0].curve[1].y = 1.0f;
+
+ part->clumpcurve = cumap;
+}
+
+void BKE_particlesettings_rough_curve_init(ParticleSettings *part)
+{
+ CurveMapping *cumap = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+
+ cumap->cm[0].curve[0].x = 0.0f;
+ cumap->cm[0].curve[0].y = 1.0f;
+ cumap->cm[0].curve[1].x = 1.0f;
+ cumap->cm[0].curve[1].y = 1.0f;
+
+ part->roughcurve = cumap;
+}
+
+ParticleSettings *BKE_particlesettings_copy(Main *bmain, ParticleSettings *part)
+{
+ ParticleSettings *partn;
+ int a;
+
+ partn = BKE_libblock_copy(bmain, &part->id);
+
+ partn->pd = MEM_dupallocN(part->pd);
+ partn->pd2 = MEM_dupallocN(part->pd2);
+ partn->effector_weights = MEM_dupallocN(part->effector_weights);
+ partn->fluid = MEM_dupallocN(part->fluid);
+
+ if (part->clumpcurve)
+ partn->clumpcurve = curvemapping_copy(part->clumpcurve);
+ if (part->roughcurve)
+ partn->roughcurve = curvemapping_copy(part->roughcurve);
+
+ partn->boids = boid_copy_settings(part->boids);
+
+ for (a = 0; a < MAX_MTEX; a++) {
+ if (part->mtex[a]) {
+ partn->mtex[a] = MEM_mallocN(sizeof(MTex), "psys_copy_tex");
+ memcpy(partn->mtex[a], part->mtex[a], sizeof(MTex));
+ id_us_plus((ID *)partn->mtex[a]->tex);
+ }
+ }
+
+ BLI_duplicatelist(&partn->dupliweights, &part->dupliweights);
+
+ BKE_id_copy_ensure_local(bmain, &part->id, &partn->id);
+
+ return partn;
+}
+
+void BKE_particlesettings_make_local(Main *bmain, ParticleSettings *part, const bool lib_local)
+{
+ BKE_id_make_local_generic(bmain, &part->id, true, lib_local);
+}
+
+/************************************************/
+/* Textures */
+/************************************************/
+
+static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, const float fuv[4], char *name, float *texco)
+{
+ MFace *mf;
+ MTFace *tf;
+ int i;
+
+ tf = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, name);
+
+ if (tf == NULL)
+ tf = CustomData_get_layer(&dm->faceData, CD_MTFACE);
+
+ if (tf == NULL)
+ return 0;
+
+ if (pa) {
+ i = ELEM(pa->num_dmcache, DMCACHE_NOTFOUND, DMCACHE_ISCHILD) ? pa->num : pa->num_dmcache;
+ if (i >= dm->getNumTessFaces(dm))
+ i = -1;
+ }
+ else
+ i = face_index;
+
+ if (i == -1) {
+ texco[0] = 0.0f;
+ texco[1] = 0.0f;
+ texco[2] = 0.0f;
+ }
+ else {
+ mf = dm->getTessFaceData(dm, i, CD_MFACE);
+
+ psys_interpolate_uvs(&tf[i], mf->v4, fuv, texco);
+
+ texco[0] = texco[0] * 2.0f - 1.0f;
+ texco[1] = texco[1] * 2.0f - 1.0f;
+ texco[2] = 0.0f;
+ }
+
+ return 1;
+}
+
+#define SET_PARTICLE_TEXTURE(type, pvalue, texfac) \
+ if ((event & mtex->mapto) & type) { \
+ pvalue = texture_value_blend(def, pvalue, value, texfac, blend); \
+ } (void)0
+
+#define CLAMP_PARTICLE_TEXTURE_POS(type, pvalue) \
+ if (event & type) { \
+ CLAMP(pvalue, 0.0f, 1.0f); \
+ } (void)0
+
+#define CLAMP_WARP_PARTICLE_TEXTURE_POS(type, pvalue) \
+ if (event & type) { \
+ if (pvalue < 0.0f) \
+ pvalue = 1.0f + pvalue; \
+ CLAMP(pvalue, 0.0f, 1.0f); \
+ } (void)0
+
+#define CLAMP_PARTICLE_TEXTURE_POSNEG(type, pvalue) \
+ if (event & type) { \
+ CLAMP(pvalue, -1.0f, 1.0f); \
+ } (void)0
+
+static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSettings *part, ParticleData *par, int child_index, int face_index, const float fw[4], float *orco, ParticleTexture *ptex, int event, float cfra)
+{
+ MTex *mtex, **mtexp = part->mtex;
+ int m;
+ float value, rgba[4], texvec[3];
+
+ ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp =
+ ptex->gravity = ptex->field = ptex->time = ptex->clump = ptex->kink_freq = ptex->kink_amp =
+ ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f;
+
+ ptex->length = 1.0f - part->randlength * psys_frand(psys, child_index + 26);
+ ptex->length *= part->clength_thres < psys_frand(psys, child_index + 27) ? part->clength : 1.0f;
+
+ for (m = 0; m < MAX_MTEX; m++, mtexp++) {
+ mtex = *mtexp;
+ if (mtex && mtex->tex && mtex->mapto) {
+ float def = mtex->def_var;
+ short blend = mtex->blendtype;
+ short texco = mtex->texco;
+
+ if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID))
+ texco = TEXCO_GLOB;
+
+ switch (texco) {
+ case TEXCO_GLOB:
+ copy_v3_v3(texvec, par->state.co);
+ break;
+ case TEXCO_OBJECT:
+ copy_v3_v3(texvec, par->state.co);
+ if (mtex->object)
+ mul_m4_v3(mtex->object->imat, texvec);
+ break;
+ case TEXCO_UV:
+ if (fw && get_particle_uv(dm, NULL, face_index, fw, mtex->uvname, texvec))
+ break;
+ /* no break, failed to get uv's, so let's try orco's */
+ case TEXCO_ORCO:
+ copy_v3_v3(texvec, orco);
+ break;
+ case TEXCO_PARTICLE:
+ /* texture coordinates in range [-1, 1] */
+ texvec[0] = 2.f * (cfra - par->time) / (par->dietime - par->time) - 1.f;
+ texvec[1] = 0.f;
+ texvec[2] = 0.f;
+ break;
+ }
+
+ externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false);
+
+ if ((event & mtex->mapto) & PAMAP_ROUGH)
+ ptex->rough1 = ptex->rough2 = ptex->roughe = texture_value_blend(def, ptex->rough1, value, mtex->roughfac, blend);
+
+ SET_PARTICLE_TEXTURE(PAMAP_LENGTH, ptex->length, mtex->lengthfac);
+ SET_PARTICLE_TEXTURE(PAMAP_CLUMP, ptex->clump, mtex->clumpfac);
+ SET_PARTICLE_TEXTURE(PAMAP_KINK_AMP, ptex->kink_amp, mtex->kinkampfac);
+ SET_PARTICLE_TEXTURE(PAMAP_KINK_FREQ, ptex->kink_freq, mtex->kinkfac);
+ SET_PARTICLE_TEXTURE(PAMAP_DENS, ptex->exist, mtex->padensfac);
+ }
+ }
+
+ CLAMP_PARTICLE_TEXTURE_POS(PAMAP_LENGTH, ptex->length);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_CLUMP, ptex->clump);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_KINK_AMP, ptex->kink_amp);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_KINK_FREQ, ptex->kink_freq);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_ROUGH, ptex->rough1);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_DENS, ptex->exist);
+}
+void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra)
+{
+ Object *ob = sim->ob;
+ Mesh *me = (Mesh *)ob->data;
+ ParticleSettings *part = sim->psys->part;
+ MTex **mtexp = part->mtex;
+ MTex *mtex;
+ int m;
+ float value, rgba[4], co[3], texvec[3];
+ int setvars = 0;
+
+ /* initialize ptex */
+ ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp =
+ ptex->gravity = ptex->field = ptex->length = ptex->clump = ptex->kink_freq = ptex->kink_amp =
+ ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f;
+
+ ptex->time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart;
+
+ for (m = 0; m < MAX_MTEX; m++, mtexp++) {
+ mtex = *mtexp;
+ if (mtex && mtex->tex && mtex->mapto) {
+ float def = mtex->def_var;
+ short blend = mtex->blendtype;
+ short texco = mtex->texco;
+
+ if (texco == TEXCO_UV && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID))
+ texco = TEXCO_GLOB;
+
+ switch (texco) {
+ case TEXCO_GLOB:
+ copy_v3_v3(texvec, pa->state.co);
+ break;
+ case TEXCO_OBJECT:
+ copy_v3_v3(texvec, pa->state.co);
+ if (mtex->object)
+ mul_m4_v3(mtex->object->imat, texvec);
+ break;
+ case TEXCO_UV:
+ if (get_particle_uv(sim->psmd->dm_final, pa, 0, pa->fuv, mtex->uvname, texvec))
+ break;
+ /* no break, failed to get uv's, so let's try orco's */
+ case TEXCO_ORCO:
+ psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0);
+
+ if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) {
+ BKE_mesh_texspace_calc(me);
+ }
+ sub_v3_v3(texvec, me->loc);
+ if (me->size[0] != 0.0f) texvec[0] /= me->size[0];
+ if (me->size[1] != 0.0f) texvec[1] /= me->size[1];
+ if (me->size[2] != 0.0f) texvec[2] /= me->size[2];
+ break;
+ case TEXCO_PARTICLE:
+ /* texture coordinates in range [-1, 1] */
+ texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f;
+ if (sim->psys->totpart > 0)
+ texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f;
+ else
+ texvec[1] = 0.0f;
+ texvec[2] = 0.f;
+ break;
+ }
+
+ externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false);
+
+ if ((event & mtex->mapto) & PAMAP_TIME) {
+ /* the first time has to set the base value for time regardless of blend mode */
+ if ((setvars & MAP_PA_TIME) == 0) {
+ int flip = (mtex->timefac < 0.0f);
+ float timefac = fabsf(mtex->timefac);
+ ptex->time *= 1.0f - timefac;
+ ptex->time += timefac * ((flip) ? 1.0f - value : value);
+ setvars |= MAP_PA_TIME;
+ }
+ else
+ ptex->time = texture_value_blend(def, ptex->time, value, mtex->timefac, blend);
+ }
+ SET_PARTICLE_TEXTURE(PAMAP_LIFE, ptex->life, mtex->lifefac);
+ SET_PARTICLE_TEXTURE(PAMAP_DENS, ptex->exist, mtex->padensfac);
+ SET_PARTICLE_TEXTURE(PAMAP_SIZE, ptex->size, mtex->sizefac);
+ SET_PARTICLE_TEXTURE(PAMAP_IVEL, ptex->ivel, mtex->ivelfac);
+ SET_PARTICLE_TEXTURE(PAMAP_FIELD, ptex->field, mtex->fieldfac);
+ SET_PARTICLE_TEXTURE(PAMAP_GRAVITY, ptex->gravity, mtex->gravityfac);
+ SET_PARTICLE_TEXTURE(PAMAP_DAMP, ptex->damp, mtex->dampfac);
+ SET_PARTICLE_TEXTURE(PAMAP_LENGTH, ptex->length, mtex->lengthfac);
+ }
+ }
+
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_TIME, ptex->time);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_LIFE, ptex->life);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_DENS, ptex->exist);
+ CLAMP_PARTICLE_TEXTURE_POS(PAMAP_SIZE, ptex->size);
+ CLAMP_PARTICLE_TEXTURE_POSNEG(PAMAP_IVEL, ptex->ivel);
+ CLAMP_PARTICLE_TEXTURE_POSNEG(PAMAP_FIELD, ptex->field);
+ CLAMP_PARTICLE_TEXTURE_POSNEG(PAMAP_GRAVITY, ptex->gravity);
+ CLAMP_WARP_PARTICLE_TEXTURE_POS(PAMAP_DAMP, ptex->damp);
+ CLAMP_PARTICLE_TEXTURE_POS(PAMAP_LENGTH, ptex->length);
+}
+/************************************************/
+/* Particle State */
+/************************************************/
+float psys_get_timestep(ParticleSimulationData *sim)
+{
+ return 0.04f * sim->psys->part->timetweak;
+}
+float psys_get_child_time(ParticleSystem *psys, ChildParticle *cpa, float cfra, float *birthtime, float *dietime)
+{
+ ParticleSettings *part = psys->part;
+ float time, life;
+
+ if (part->childtype == PART_CHILD_FACES) {
+ int w = 0;
+ time = 0.0;
+ while (w < 4 && cpa->pa[w] >= 0) {
+ time += cpa->w[w] * (psys->particles + cpa->pa[w])->time;
+ w++;
+ }
+
+ life = part->lifetime * (1.0f - part->randlife * psys_frand(psys, cpa - psys->child + 25));
+ }
+ else {
+ ParticleData *pa = psys->particles + cpa->parent;
+
+ time = pa->time;
+ life = pa->lifetime;
+ }
+
+ if (birthtime)
+ *birthtime = time;
+ if (dietime)
+ *dietime = time + life;
+
+ return (cfra - time) / life;
+}
+float psys_get_child_size(ParticleSystem *psys, ChildParticle *cpa, float UNUSED(cfra), float *UNUSED(pa_time))
+{
+ ParticleSettings *part = psys->part;
+ float size; // time XXX
+
+ if (part->childtype == PART_CHILD_FACES)
+ size = part->size;
+ else
+ size = psys->particles[cpa->parent].size;
+
+ size *= part->childsize;
+
+ if (part->childrandsize != 0.0f)
+ size *= 1.0f - part->childrandsize * psys_frand(psys, cpa - psys->child + 26);
+
+ return size;
+}
+static void get_child_modifier_parameters(ParticleSettings *part, ParticleThreadContext *ctx, ChildParticle *cpa, short cpa_from, int cpa_num, float *cpa_fuv, float *orco, ParticleTexture *ptex)
+{
+ ParticleSystem *psys = ctx->sim.psys;
+ int i = cpa - psys->child;
+
+ get_cpa_texture(ctx->dm, psys, part, psys->particles + cpa->pa[0], i, cpa_num, cpa_fuv, orco, ptex, PAMAP_DENS | PAMAP_CHILD, psys->cfra);
+
+
+ if (ptex->exist < psys_frand(psys, i + 24))
+ return;
+
+ if (ctx->vg_length)
+ ptex->length *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_length);
+ if (ctx->vg_clump)
+ ptex->clump *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_clump);
+ if (ctx->vg_kink)
+ ptex->kink_freq *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_kink);
+ if (ctx->vg_rough1)
+ ptex->rough1 *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_rough1);
+ if (ctx->vg_rough2)
+ ptex->rough2 *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_rough2);
+ if (ctx->vg_roughe)
+ ptex->roughe *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_roughe);
+ if (ctx->vg_effector)
+ ptex->effector *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_effector);
+}
+/* get's hair (or keyed) particles state at the "path time" specified in state->time */
+void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *state, const bool vel)
+{
+ PARTICLE_PSMD;
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = sim->psys->part;
+ Material *ma = give_current_material(sim->ob, part->omat);
+ ParticleData *pa;
+ ChildParticle *cpa;
+ ParticleTexture ptex;
+ ParticleKey *par = 0, keys[4], tstate;
+ ParticleThreadContext ctx; /* fake thread context for child modifiers */
+ ParticleInterpolationData pind;
+
+ float t;
+ float co[3], orco[3];
+ float hairmat[4][4];
+ int totpart = psys->totpart;
+ int totchild = psys->totchild;
+ short between = 0, edit = 0;
+
+ int keyed = part->phystype & PART_PHYS_KEYED && psys->flag & PSYS_KEYED;
+ int cached = !keyed && part->type != PART_HAIR;
+
+ float *cpa_fuv; int cpa_num; short cpa_from;
+
+ /* initialize keys to zero */
+ memset(keys, 0, 4 * sizeof(ParticleKey));
+
+ t = state->time;
+ CLAMP(t, 0.0f, 1.0f);
+
+ if (p < totpart) {
+ /* interpolate pathcache directly if it exist */
+ if (psys->pathcache) {
+ ParticleCacheKey result;
+ interpolate_pathcache(psys->pathcache[p], t, &result);
+ copy_v3_v3(state->co, result.co);
+ copy_v3_v3(state->vel, result.vel);
+ copy_qt_qt(state->rot, result.rot);
+ }
+ /* otherwise interpolate with other means */
+ else {
+ pa = psys->particles + p;
+
+ pind.keyed = keyed;
+ pind.cache = cached ? psys->pointcache : NULL;
+ pind.epoint = NULL;
+ pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE);
+ /* pind.dm disabled in editmode means we don't get effectors taken into
+ * account when subdividing for instance */
+ pind.dm = psys_in_edit_mode(sim->scene, psys) ? NULL : psys->hair_out_dm;
+ init_particle_interpolation(sim->ob, psys, pa, &pind);
+ do_particle_interpolation(psys, p, pa, t, &pind, state);
+
+ if (pind.dm) {
+ mul_m4_v3(sim->ob->obmat, state->co);
+ mul_mat3_m4_v3(sim->ob->obmat, state->vel);
+ }
+ else if (!keyed && !cached && !(psys->flag & PSYS_GLOBAL_HAIR)) {
+ if ((pa->flag & PARS_REKEY) == 0) {
+ psys_mat_hair_to_global(sim->ob, sim->psmd->dm_final, part->from, pa, hairmat);
+ mul_m4_v3(hairmat, state->co);
+ mul_mat3_m4_v3(hairmat, state->vel);
+
+ if (sim->psys->effectors && (part->flag & PART_CHILD_GUIDE) == 0) {
+ do_guides(sim->psys->part, sim->psys->effectors, state, p, state->time);
+ /* TODO: proper velocity handling */
+ }
+
+ if (psys->lattice_deform_data && edit == 0)
+ calc_latt_deform(psys->lattice_deform_data, state->co, 1.0f);
+ }
+ }
+ }
+ }
+ else if (totchild) {
+ //invert_m4_m4(imat, ob->obmat);
+
+ /* interpolate childcache directly if it exists */
+ if (psys->childcache) {
+ ParticleCacheKey result;
+ interpolate_pathcache(psys->childcache[p - totpart], t, &result);
+ copy_v3_v3(state->co, result.co);
+ copy_v3_v3(state->vel, result.vel);
+ copy_qt_qt(state->rot, result.rot);
+ }
+ else {
+ float par_co[3], par_orco[3];
+
+ cpa = psys->child + p - totpart;
+
+ if (state->time < 0.0f)
+ t = psys_get_child_time(psys, cpa, -state->time, NULL, NULL);
+
+ if (totchild && part->childtype == PART_CHILD_FACES) {
+ /* part->parents could still be 0 so we can't test with totparent */
+ between = 1;
+ }
+ if (between) {
+ int w = 0;
+ float foffset;
+
+ /* get parent states */
+ while (w < 4 && cpa->pa[w] >= 0) {
+ keys[w].time = state->time;
+ psys_get_particle_on_path(sim, cpa->pa[w], keys + w, 1);
+ w++;
+ }
+
+ /* get the original coordinates (orco) for texture usage */
+ cpa_num = cpa->num;
+
+ foffset = cpa->foffset;
+ cpa_fuv = cpa->fuv;
+ cpa_from = PART_FROM_FACE;
+
+ psys_particle_on_emitter(psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa->fuv, foffset, co, 0, 0, 0, orco, 0);
+
+ /* we need to save the actual root position of the child for positioning it accurately to the surface of the emitter */
+ //copy_v3_v3(cpa_1st, co);
+
+ //mul_m4_v3(ob->obmat, cpa_1st);
+
+ pa = psys->particles + cpa->parent;
+
+ psys_particle_on_emitter(psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, par_co, 0, 0, 0, par_orco, 0);
+ if (part->type == PART_HAIR)
+ psys_mat_hair_to_global(sim->ob, sim->psmd->dm_final, psys->part->from, pa, hairmat);
+ else
+ unit_m4(hairmat);
+
+ pa = 0;
+ }
+ else {
+ /* get the parent state */
+ keys->time = state->time;
+ psys_get_particle_on_path(sim, cpa->parent, keys, 1);
+
+ /* get the original coordinates (orco) for texture usage */
+ pa = psys->particles + cpa->parent;
+
+ cpa_from = part->from;
+ cpa_num = pa->num;
+ cpa_fuv = pa->fuv;
+
+ psys_particle_on_emitter(psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, par_co, 0, 0, 0, par_orco, 0);
+ if (part->type == PART_HAIR) {
+ psys_particle_on_emitter(psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa_fuv, pa->foffset, co, 0, 0, 0, orco, 0);
+ psys_mat_hair_to_global(sim->ob, sim->psmd->dm_final, psys->part->from, pa, hairmat);
+ }
+ else {
+ copy_v3_v3(orco, cpa->fuv);
+ unit_m4(hairmat);
+ }
+ }
+
+ /* correct child ipo timing */
+#if 0 // XXX old animation system
+ if ((part->flag & PART_ABS_TIME) == 0 && part->ipo) {
+ calc_ipo(part->ipo, 100.0f * t);
+ execute_ipo((ID *)part, part->ipo);
+ }
+#endif // XXX old animation system
+
+ /* get different child parameters from textures & vgroups */
+ memset(&ctx, 0, sizeof(ParticleThreadContext));
+ ctx.sim = *sim;
+ ctx.dm = psmd->dm_final;
+ ctx.ma = ma;
+ /* TODO: assign vertex groups */
+ get_child_modifier_parameters(part, &ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex);
+
+ if (between) {
+ int w = 0;
+
+ state->co[0] = state->co[1] = state->co[2] = 0.0f;
+ state->vel[0] = state->vel[1] = state->vel[2] = 0.0f;
+
+ /* child position is the weighted sum of parent positions */
+ while (w < 4 && cpa->pa[w] >= 0) {
+ state->co[0] += cpa->w[w] * keys[w].co[0];
+ state->co[1] += cpa->w[w] * keys[w].co[1];
+ state->co[2] += cpa->w[w] * keys[w].co[2];
+
+ state->vel[0] += cpa->w[w] * keys[w].vel[0];
+ state->vel[1] += cpa->w[w] * keys[w].vel[1];
+ state->vel[2] += cpa->w[w] * keys[w].vel[2];
+ w++;
+ }
+ /* apply offset for correct positioning */
+ //add_v3_v3(state->co, cpa_1st);
+ }
+ else {
+ /* offset the child from the parent position */
+ offset_child(cpa, keys, keys->rot, state, part->childflat, part->childrad);
+ }
+
+ par = keys;
+
+ if (vel)
+ copy_particle_key(&tstate, state, 1);
+
+ /* apply different deformations to the child path */
+ do_child_modifiers(NULL, sim, &ptex, par->co, par->vel, par->rot, par_orco, cpa, orco, hairmat, state, t);
+
+ /* try to estimate correct velocity */
+ if (vel) {
+ ParticleKey tstate_tmp;
+ float length = len_v3(state->vel);
+
+ if (t >= 0.001f) {
+ tstate_tmp.time = t - 0.001f;
+ psys_get_particle_on_path(sim, p, &tstate_tmp, 0);
+ sub_v3_v3v3(state->vel, state->co, tstate_tmp.co);
+ normalize_v3(state->vel);
+ }
+ else {
+ tstate_tmp.time = t + 0.001f;
+ psys_get_particle_on_path(sim, p, &tstate_tmp, 0);
+ sub_v3_v3v3(state->vel, tstate_tmp.co, state->co);
+ normalize_v3(state->vel);
+ }
+
+ mul_v3_fl(state->vel, length);
+ }
+ }
+ }
+}
+/* gets particle's state at a time, returns 1 if particle exists and can be seen and 0 if not */
+int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *state, int always)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleData *pa = NULL;
+ ChildParticle *cpa = NULL;
+ float cfra;
+ int totpart = psys->totpart;
+ float timestep = psys_get_timestep(sim);
+
+ /* negative time means "use current time" */
+ cfra = state->time > 0 ? state->time : BKE_scene_frame_get(sim->scene);
+
+ if (p >= totpart) {
+ if (!psys->totchild)
+ return 0;
+
+ if (part->childtype == PART_CHILD_FACES) {
+ if (!(psys->flag & PSYS_KEYED))
+ return 0;
+
+ cpa = psys->child + p - totpart;
+
+ state->time = psys_get_child_time(psys, cpa, cfra, NULL, NULL);
+
+ if (!always) {
+ if ((state->time < 0.0f && !(part->flag & PART_UNBORN)) ||
+ (state->time > 1.0f && !(part->flag & PART_DIED)))
+ {
+ return 0;
+ }
+ }
+
+ state->time = (cfra - (part->sta + (part->end - part->sta) * psys_frand(psys, p + 23))) / (part->lifetime * psys_frand(psys, p + 24));
+
+ psys_get_particle_on_path(sim, p, state, 1);
+ return 1;
+ }
+ else {
+ cpa = sim->psys->child + p - totpart;
+ pa = sim->psys->particles + cpa->parent;
+ }
+ }
+ else {
+ pa = sim->psys->particles + p;
+ }
+
+ if (pa) {
+ if (!always) {
+ if ((cfra < pa->time && (part->flag & PART_UNBORN) == 0) ||
+ (cfra >= pa->dietime && (part->flag & PART_DIED) == 0))
+ {
+ return 0;
+ }
+ }
+
+ cfra = MIN2(cfra, pa->dietime);
+ }
+
+ if (sim->psys->flag & PSYS_KEYED) {
+ state->time = -cfra;
+ psys_get_particle_on_path(sim, p, state, 1);
+ return 1;
+ }
+ else {
+ if (cpa) {
+ float mat[4][4];
+ ParticleKey *key1;
+ float t = (cfra - pa->time) / pa->lifetime;
+ float par_orco[3] = {0.0f, 0.0f, 0.0f};
+
+ key1 = &pa->state;
+ offset_child(cpa, key1, key1->rot, state, part->childflat, part->childrad);
+
+ CLAMP(t, 0.0f, 1.0f);
+
+ unit_m4(mat);
+ do_child_modifiers(NULL, sim, NULL, key1->co, key1->vel, key1->rot, par_orco, cpa, cpa->fuv, mat, state, t);
+
+ if (psys->lattice_deform_data)
+ calc_latt_deform(psys->lattice_deform_data, state->co, 1.0f);
+ }
+ else {
+ if (pa->state.time == cfra || ELEM(part->phystype, PART_PHYS_NO, PART_PHYS_KEYED))
+ copy_particle_key(state, &pa->state, 1);
+ else if (pa->prev_state.time == cfra)
+ copy_particle_key(state, &pa->prev_state, 1);
+ else {
+ float dfra, frs_sec = sim->scene->r.frs_sec;
+ /* let's interpolate to try to be as accurate as possible */
+ if (pa->state.time + 2.f >= state->time && pa->prev_state.time - 2.f <= state->time) {
+ if (pa->prev_state.time >= pa->state.time || pa->prev_state.time < 0.f) {
+ /* prev_state is wrong so let's not use it, this can happen at frames 1, 0 or particle birth */
+ dfra = state->time - pa->state.time;
+
+ copy_particle_key(state, &pa->state, 1);
+
+ madd_v3_v3v3fl(state->co, state->co, state->vel, dfra / frs_sec);
+ }
+ else {
+ ParticleKey keys[4];
+ float keytime;
+
+ copy_particle_key(keys + 1, &pa->prev_state, 1);
+ copy_particle_key(keys + 2, &pa->state, 1);
+
+ dfra = keys[2].time - keys[1].time;
+
+ keytime = (state->time - keys[1].time) / dfra;
+
+ /* convert velocity to timestep size */
+ mul_v3_fl(keys[1].vel, dfra * timestep);
+ mul_v3_fl(keys[2].vel, dfra * timestep);
+
+ psys_interpolate_particle(-1, keys, keytime, state, 1);
+
+ /* convert back to real velocity */
+ mul_v3_fl(state->vel, 1.f / (dfra * timestep));
+
+ interp_v3_v3v3(state->ave, keys[1].ave, keys[2].ave, keytime);
+ interp_qt_qtqt(state->rot, keys[1].rot, keys[2].rot, keytime);
+ }
+ }
+ else if (pa->state.time + 1.f >= state->time && pa->state.time - 1.f <= state->time) {
+ /* linear interpolation using only pa->state */
+
+ dfra = state->time - pa->state.time;
+
+ copy_particle_key(state, &pa->state, 1);
+
+ madd_v3_v3v3fl(state->co, state->co, state->vel, dfra / frs_sec);
+ }
+ else {
+ /* extrapolating over big ranges is not accurate so let's just give something close to reasonable back */
+ copy_particle_key(state, &pa->state, 0);
+ }
+ }
+
+ if (sim->psys->lattice_deform_data)
+ calc_latt_deform(sim->psys->lattice_deform_data, state->co, 1.0f);
+ }
+
+ return 1;
+ }
+}
+
+void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part,
+ ParticleSystemModifierData *psmd, ParticleData *pa, ChildParticle *cpa,
+ float uv[2], float orco[3])
+{
+ MFace *mface;
+ MTFace *mtface;
+ float loc[3];
+ int num;
+
+ /* XXX: on checking '(psmd->dm != NULL)'
+ * This is incorrect but needed for metaball evaluation.
+ * Ideally this would be calculated via the depsgraph, however with metaballs,
+ * the entire scenes dupli's are scanned, which also looks into uncalculated data.
+ *
+ * For now just include this workaround as an alternative to crashing,
+ * but longer term metaballs should behave in a more manageable way, see: T46622. */
+
+ uv[0] = uv[1] = 0.f;
+
+ /* Grid distribution doesn't support UV or emit from vertex mode */
+ bool is_grid = (part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT);
+
+ if (cpa) {
+ if ((part->childtype == PART_CHILD_FACES) && (psmd->dm_final != NULL)) {
+ CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final);
+ const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE);
+ mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx);
+
+ if (mtface && !is_grid) {
+ mface = psmd->dm_final->getTessFaceData(psmd->dm_final, cpa->num, CD_MFACE);
+ mtface += cpa->num;
+ psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv);
+ }
+
+ psys_particle_on_emitter(psmd, PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, loc, 0, 0, 0, orco, 0);
+ return;
+ }
+ else {
+ pa = psys->particles + cpa->pa[0];
+ }
+ }
+
+ if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL) && !is_grid) {
+ CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final);
+ const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE);
+ mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx);
+
+ num = pa->num_dmcache;
+
+ if (num == DMCACHE_NOTFOUND)
+ num = pa->num;
+
+ if (num >= psmd->dm_final->getNumTessFaces(psmd->dm_final)) {
+ /* happens when simplify is enabled
+ * gives invalid coords but would crash otherwise */
+ num = DMCACHE_NOTFOUND;
+ }
+
+ if (mtface && !ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ mface = psmd->dm_final->getTessFaceData(psmd->dm_final, num, CD_MFACE);
+ mtface += num;
+ psys_interpolate_uvs(mtface, mface->v4, pa->fuv, uv);
+ }
+ }
+
+ psys_particle_on_emitter(psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, loc, 0, 0, 0, orco, 0);
+}
+
+void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa, ChildParticle *cpa, ParticleCacheKey *cache, float mat[4][4], float *scale)
+{
+ Object *ob = sim->ob;
+ ParticleSystem *psys = sim->psys;
+ ParticleSystemModifierData *psmd = sim->psmd;
+ float loc[3], nor[3], vec[3], side[3], len;
+ float xvec[3] = {-1.0, 0.0, 0.0}, nmat[3][3];
+
+ sub_v3_v3v3(vec, (cache + cache->segments)->co, cache->co);
+ len = normalize_v3(vec);
+
+ if (pa == NULL && psys->part->childflat != PART_CHILD_FACES)
+ pa = psys->particles + cpa->pa[0];
+
+ if (pa)
+ psys_particle_on_emitter(psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, loc, nor, 0, 0, 0, 0);
+ else
+ psys_particle_on_emitter(psmd, PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, loc, nor, 0, 0, 0, 0);
+
+ if (psys->part->rotmode == PART_ROT_VEL) {
+ transpose_m3_m4(nmat, ob->imat);
+ mul_m3_v3(nmat, nor);
+ normalize_v3(nor);
+
+ /* make sure that we get a proper side vector */
+ if (fabsf(dot_v3v3(nor, vec)) > 0.999999f) {
+ if (fabsf(dot_v3v3(nor, xvec)) > 0.999999f) {
+ nor[0] = 0.0f;
+ nor[1] = 1.0f;
+ nor[2] = 0.0f;
+ }
+ else {
+ nor[0] = 1.0f;
+ nor[1] = 0.0f;
+ nor[2] = 0.0f;
+ }
+ }
+ cross_v3_v3v3(side, nor, vec);
+ normalize_v3(side);
+
+ /* rotate side vector around vec */
+ if (psys->part->phasefac != 0) {
+ float q_phase[4];
+ float phasefac = psys->part->phasefac;
+ if (psys->part->randphasefac != 0.0f)
+ phasefac += psys->part->randphasefac * psys_frand(psys, (pa - psys->particles) + 20);
+ axis_angle_to_quat(q_phase, vec, phasefac * (float)M_PI);
+
+ mul_qt_v3(q_phase, side);
+ }
+
+ cross_v3_v3v3(nor, vec, side);
+
+ unit_m4(mat);
+ copy_v3_v3(mat[0], vec);
+ copy_v3_v3(mat[1], side);
+ copy_v3_v3(mat[2], nor);
+ }
+ else {
+ quat_to_mat4(mat, pa->state.rot);
+ }
+
+ *scale = len;
+}
+
+void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3])
+{
+ float onevec[3] = {0.0f, 0.0f, 0.0f}, tvec[3], tvec2[3];
+
+ xvec[0] = 1.0f; xvec[1] = 0.0f; xvec[2] = 0.0f;
+ yvec[0] = 0.0f; yvec[1] = 1.0f; yvec[2] = 0.0f;
+
+ /* can happen with bad pointcache or physics calculation
+ * since this becomes geometry, nan's and inf's crash raytrace code.
+ * better not allow this. */
+ if ((!isfinite(bb->vec[0])) || (!isfinite(bb->vec[1])) || (!isfinite(bb->vec[2])) ||
+ (!isfinite(bb->vel[0])) || (!isfinite(bb->vel[1])) || (!isfinite(bb->vel[2])) )
+ {
+ zero_v3(bb->vec);
+ zero_v3(bb->vel);
+
+ zero_v3(xvec);
+ zero_v3(yvec);
+ zero_v3(zvec);
+ zero_v3(center);
+
+ return;
+ }
+
+ if (bb->align < PART_BB_VIEW)
+ onevec[bb->align] = 1.0f;
+
+ if (bb->lock && (bb->align == PART_BB_VIEW)) {
+ normalize_v3_v3(xvec, bb->ob->obmat[0]);
+ normalize_v3_v3(yvec, bb->ob->obmat[1]);
+ normalize_v3_v3(zvec, bb->ob->obmat[2]);
+ }
+ else if (bb->align == PART_BB_VEL) {
+ float temp[3];
+
+ normalize_v3_v3(temp, bb->vel);
+
+ sub_v3_v3v3(zvec, bb->ob->obmat[3], bb->vec);
+
+ if (bb->lock) {
+ float fac = -dot_v3v3(zvec, temp);
+
+ madd_v3_v3fl(zvec, temp, fac);
+ }
+ normalize_v3(zvec);
+
+ cross_v3_v3v3(xvec, temp, zvec);
+ normalize_v3(xvec);
+
+ cross_v3_v3v3(yvec, zvec, xvec);
+ }
+ else {
+ sub_v3_v3v3(zvec, bb->ob->obmat[3], bb->vec);
+ if (bb->lock)
+ zvec[bb->align] = 0.0f;
+ normalize_v3(zvec);
+
+ if (bb->align < PART_BB_VIEW)
+ cross_v3_v3v3(xvec, onevec, zvec);
+ else
+ cross_v3_v3v3(xvec, bb->ob->obmat[1], zvec);
+ normalize_v3(xvec);
+
+ cross_v3_v3v3(yvec, zvec, xvec);
+ }
+
+ copy_v3_v3(tvec, xvec);
+ copy_v3_v3(tvec2, yvec);
+
+ mul_v3_fl(xvec, cosf(bb->tilt * (float)M_PI));
+ mul_v3_fl(tvec2, sinf(bb->tilt * (float)M_PI));
+ add_v3_v3(xvec, tvec2);
+
+ mul_v3_fl(yvec, cosf(bb->tilt * (float)M_PI));
+ mul_v3_fl(tvec, -sinf(bb->tilt * (float)M_PI));
+ add_v3_v3(yvec, tvec);
+
+ mul_v3_fl(xvec, bb->size[0]);
+ mul_v3_fl(yvec, bb->size[1]);
+
+ madd_v3_v3v3fl(center, bb->vec, xvec, bb->offset[0]);
+ madd_v3_v3fl(center, yvec, bb->offset[1]);
+}
+
+void psys_apply_hair_lattice(Scene *scene, Object *ob, ParticleSystem *psys)
+{
+ ParticleSimulationData sim = {0};
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(ob, psys);
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ if (psys->lattice_deform_data) {
+ ParticleData *pa = psys->particles;
+ HairKey *hkey;
+ int p, h;
+ float hairmat[4][4], imat[4][4];
+
+ for (p = 0; p < psys->totpart; p++, pa++) {
+ psys_mat_hair_to_global(sim.ob, sim.psmd->dm_final, psys->part->from, pa, hairmat);
+ invert_m4_m4(imat, hairmat);
+
+ hkey = pa->hair;
+ for (h = 0; h < pa->totkey; h++, hkey++) {
+ mul_m4_v3(hairmat, hkey->co);
+ calc_latt_deform(psys->lattice_deform_data, hkey->co, 1.0f);
+ mul_m4_v3(imat, hkey->co);
+ }
+ }
+
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+
+ /* protect the applied shape */
+ psys->flag |= PSYS_EDITED;
+ }
+}
diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c
new file mode 100644
index 00000000000..842de869291
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle_child.c
@@ -0,0 +1,739 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/particle_child.c
+ * \ingroup bke
+ */
+
+#include "BLI_math.h"
+#include "BLI_noise.h"
+
+#include "DNA_material_types.h"
+
+#include "BKE_colortools.h"
+#include "BKE_particle.h"
+
+struct Material;
+
+void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat,
+ short type, short axis, float obmat[4][4], int smooth_start);
+float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
+ bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
+void do_child_modifiers(ParticleThreadContext *ctx, ParticleSimulationData *sim,
+ ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3],
+ ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t);
+
+static void get_strand_normal(Material *ma, const float surfnor[3], float surfdist, float nor[3])
+{
+ float cross[3], nstrand[3], vnor[3], blend;
+
+ if (!((ma->mode & MA_STR_SURFDIFF) || (ma->strand_surfnor > 0.0f)))
+ return;
+
+ if (ma->mode & MA_STR_SURFDIFF) {
+ cross_v3_v3v3(cross, surfnor, nor);
+ cross_v3_v3v3(nstrand, nor, cross);
+
+ blend = dot_v3v3(nstrand, surfnor);
+ CLAMP(blend, 0.0f, 1.0f);
+
+ interp_v3_v3v3(vnor, nstrand, surfnor, blend);
+ normalize_v3(vnor);
+ }
+ else {
+ copy_v3_v3(vnor, nor);
+ }
+
+ if (ma->strand_surfnor > 0.0f) {
+ if (ma->strand_surfnor > surfdist) {
+ blend = (ma->strand_surfnor - surfdist) / ma->strand_surfnor;
+ interp_v3_v3v3(vnor, vnor, surfnor, blend);
+ normalize_v3(vnor);
+ }
+ }
+
+ copy_v3_v3(nor, vnor);
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct ParticlePathIterator {
+ ParticleCacheKey *key;
+ int index;
+ float time;
+
+ ParticleCacheKey *parent_key;
+ float parent_rotation[4];
+} ParticlePathIterator;
+
+static void psys_path_iter_get(ParticlePathIterator *iter, ParticleCacheKey *keys, int totkeys,
+ ParticleCacheKey *parent, int index)
+{
+ BLI_assert(index >= 0 && index < totkeys);
+
+ iter->key = keys + index;
+ iter->index = index;
+ iter->time = (float)index / (float)(totkeys - 1);
+
+ if (parent) {
+ iter->parent_key = parent + index;
+ if (index > 0)
+ mul_qt_qtqt(iter->parent_rotation, iter->parent_key->rot, parent->rot);
+ else
+ copy_qt_qt(iter->parent_rotation, parent->rot);
+ }
+ else {
+ iter->parent_key = NULL;
+ unit_qt(iter->parent_rotation);
+ }
+}
+
+typedef struct ParticlePathModifier {
+ struct ParticlePathModifier *next, *prev;
+
+ void (*apply)(ParticleCacheKey *keys, int totkeys, ParticleCacheKey *parent_keys);
+} ParticlePathModifier;
+
+/* ------------------------------------------------------------------------- */
+
+static void do_kink_spiral_deform(ParticleKey *state, const float dir[3], const float kink[3],
+ float time, float freq, float shape, float amplitude,
+ const float spiral_start[3])
+{
+ float result[3];
+
+ CLAMP(time, 0.f, 1.f);
+
+ copy_v3_v3(result, state->co);
+
+ {
+ /* Creates a logarithmic spiral:
+ * r(theta) = a * exp(b * theta)
+ *
+ * The "density" parameter b is defined by the shape parameter
+ * and goes up to the Golden Spiral for 1.0
+ * https://en.wikipedia.org/wiki/Golden_spiral
+ */
+ const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f;
+ /* angle of the spiral against the curve (rotated opposite to make a smooth transition) */
+ const float start_angle = ((b != 0.0f) ? atanf(1.0f / b) :
+ (float)-M_PI_2) + (b > 0.0f ? -(float)M_PI_2 : (float)M_PI_2);
+
+ float spiral_axis[3], rot[3][3];
+ float vec[3];
+
+ float theta = freq * time * 2.0f * (float)M_PI;
+ float radius = amplitude * expf(b * theta);
+
+ /* a bit more intuitive than using negative frequency for this */
+ if (amplitude < 0.0f)
+ theta = -theta;
+
+ cross_v3_v3v3(spiral_axis, dir, kink);
+ normalize_v3(spiral_axis);
+
+ mul_v3_v3fl(vec, kink, -radius);
+
+ axis_angle_normalized_to_mat3(rot, spiral_axis, theta);
+ mul_m3_v3(rot, vec);
+
+ madd_v3_v3fl(vec, kink, amplitude);
+
+ axis_angle_normalized_to_mat3(rot, spiral_axis, -start_angle);
+ mul_m3_v3(rot, vec);
+
+ add_v3_v3v3(result, spiral_start, vec);
+ }
+
+ copy_v3_v3(state->co, result);
+}
+
+static void do_kink_spiral(ParticleThreadContext *ctx, ParticleTexture *ptex, const float parent_orco[3],
+ ChildParticle *cpa, const float orco[3], float hairmat[4][4],
+ ParticleCacheKey *keys, ParticleCacheKey *parent_keys, int *r_totkeys, float *r_max_length)
+{
+ struct ParticleSettings *part = ctx->sim.psys->part;
+ const int seed = ctx->sim.psys->child_seed + (int)(cpa - ctx->sim.psys->child);
+ const int totkeys = ctx->segments + 1;
+ const int extrakeys = ctx->extra_segments;
+
+ float kink_amp_random = part->kink_amp_random;
+ float kink_amp = part->kink_amp * (1.0f - kink_amp_random * psys_frand(ctx->sim.psys, 93541 + seed));
+ float kink_freq = part->kink_freq;
+ float kink_shape = part->kink_shape;
+ float kink_axis_random = part->kink_axis_random;
+ float rough1 = part->rough1;
+ float rough2 = part->rough2;
+ float rough_end = part->rough_end;
+
+ ParticlePathIterator iter;
+ ParticleCacheKey *key;
+ int k;
+
+ float dir[3];
+ float spiral_start[3] = {0.0f, 0.0f, 0.0f};
+ float spiral_start_time = 0.0f;
+ float spiral_par_co[3] = {0.0f, 0.0f, 0.0f};
+ float spiral_par_vel[3] = {0.0f, 0.0f, 0.0f};
+ float spiral_par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f};
+ float totlen;
+ float cut_time;
+ int start_index = 0, end_index = 0;
+ float kink_base[3];
+
+ if (ptex) {
+ kink_amp *= ptex->kink_amp;
+ kink_freq *= ptex->kink_freq;
+ rough1 *= ptex->rough1;
+ rough2 *= ptex->rough2;
+ rough_end *= ptex->roughe;
+ }
+
+ cut_time = (totkeys - 1) * ptex->length;
+ zero_v3(spiral_start);
+
+ for (k = 0, key = keys; k < totkeys-1; k++, key++) {
+ if ((float)(k + 1) >= cut_time) {
+ float fac = cut_time - (float)k;
+ ParticleCacheKey *par = parent_keys + k;
+
+ start_index = k + 1;
+ end_index = start_index + extrakeys;
+
+ spiral_start_time = ((float)k + fac) / (float)(totkeys - 1);
+ interp_v3_v3v3(spiral_start, key->co, (key+1)->co, fac);
+
+ interp_v3_v3v3(spiral_par_co, par->co, (par+1)->co, fac);
+ interp_v3_v3v3(spiral_par_vel, par->vel, (par+1)->vel, fac);
+ interp_qt_qtqt(spiral_par_rot, par->rot, (par+1)->rot, fac);
+
+ break;
+ }
+ }
+
+ zero_v3(dir);
+
+ zero_v3(kink_base);
+ kink_base[part->kink_axis] = 1.0f;
+ mul_mat3_m4_v3(ctx->sim.ob->obmat, kink_base);
+
+ for (k = 0, key = keys; k < end_index; k++, key++) {
+ float par_time;
+ float *par_co, *par_vel, *par_rot;
+
+ psys_path_iter_get(&iter, keys, end_index, NULL, k);
+ if (k < start_index) {
+ sub_v3_v3v3(dir, (key+1)->co, key->co);
+ normalize_v3(dir);
+
+ par_time = (float)k / (float)(totkeys - 1);
+ par_co = parent_keys[k].co;
+ par_vel = parent_keys[k].vel;
+ par_rot = parent_keys[k].rot;
+ }
+ else {
+ float spiral_time = (float)(k - start_index) / (float)(extrakeys-1);
+ float kink[3], tmp[3];
+
+ /* use same time value for every point on the spiral */
+ par_time = spiral_start_time;
+ par_co = spiral_par_co;
+ par_vel = spiral_par_vel;
+ par_rot = spiral_par_rot;
+
+ project_v3_v3v3(tmp, kink_base, dir);
+ sub_v3_v3v3(kink, kink_base, tmp);
+ normalize_v3(kink);
+
+ if (kink_axis_random > 0.0f) {
+ float a = kink_axis_random * (psys_frand(ctx->sim.psys, 7112 + seed) * 2.0f - 1.0f) * (float)M_PI;
+ float rot[3][3];
+
+ axis_angle_normalized_to_mat3(rot, dir, a);
+ mul_m3_v3(rot, kink);
+ }
+
+ do_kink_spiral_deform((ParticleKey *)key, dir, kink, spiral_time, kink_freq, kink_shape, kink_amp, spiral_start);
+ }
+
+ /* apply different deformations to the child path */
+ do_child_modifiers(ctx, &ctx->sim, ptex, par_co, par_vel, par_rot, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, par_time);
+ }
+
+ totlen = 0.0f;
+ for (k = 0, key = keys; k < end_index-1; k++, key++)
+ totlen += len_v3v3((key+1)->co, key->co);
+
+ *r_totkeys = end_index;
+ *r_max_length = totlen;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static bool check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *key, float max_length, float step_length, float *cur_length, float dvec[3])
+{
+ if (*cur_length + step_length > max_length) {
+ sub_v3_v3v3(dvec, key->co, (key-1)->co);
+ mul_v3_fl(dvec, (max_length - *cur_length) / step_length);
+ add_v3_v3v3(key->co, (key-1)->co, dvec);
+ keys->segments = k;
+ /* something over the maximum step value */
+ return false;
+ }
+ else {
+ *cur_length += step_length;
+ return true;
+ }
+}
+
+void psys_apply_child_modifiers(ParticleThreadContext *ctx, struct ListBase *modifiers,
+ ChildParticle *cpa, ParticleTexture *ptex, const float orco[3], const float ornor[3], float hairmat[4][4],
+ ParticleCacheKey *keys, ParticleCacheKey *parent_keys, const float parent_orco[3])
+{
+ struct ParticleSettings *part = ctx->sim.psys->part;
+ struct Material *ma = ctx->ma;
+ const bool draw_col_ma = (part->draw_col == PART_DRAW_COL_MAT);
+ const bool use_length_check = !ELEM(part->kink, PART_KINK_SPIRAL);
+
+ ParticlePathModifier *mod;
+ ParticleCacheKey *key;
+ int totkeys, k;
+ float max_length;
+
+#if 0 /* TODO for the future: use true particle modifiers that work on the whole curve */
+ for (mod = modifiers->first; mod; mod = mod->next) {
+ mod->apply(keys, totkeys, parent_keys);
+ }
+#else
+ (void)modifiers;
+ (void)mod;
+
+ if (part->kink == PART_KINK_SPIRAL) {
+ do_kink_spiral(ctx, ptex, parent_orco, cpa, orco, hairmat, keys, parent_keys, &totkeys, &max_length);
+ keys->segments = totkeys - 1;
+ }
+ else {
+ ParticlePathIterator iter;
+
+ totkeys = ctx->segments + 1;
+ max_length = ptex->length;
+
+ for (k = 0, key = keys; k < totkeys; k++, key++) {
+ ParticleKey *par;
+
+ psys_path_iter_get(&iter, keys, totkeys, parent_keys, k);
+ par = (ParticleKey *)iter.parent_key;
+
+ /* apply different deformations to the child path */
+ do_child_modifiers(ctx, &ctx->sim, ptex, par->co, par->vel, iter.parent_rotation, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, iter.time);
+ }
+ }
+
+ {
+ const float step_length = 1.0f / (float)(totkeys - 1);
+
+ float cur_length = 0.0f;
+
+ /* we have to correct velocity because of kink & clump */
+ for (k = 0, key = keys; k < totkeys; ++k, ++key) {
+ if (k >= 2) {
+ sub_v3_v3v3((key-1)->vel, key->co, (key-2)->co);
+ mul_v3_fl((key-1)->vel, 0.5);
+
+ if (ma && draw_col_ma)
+ get_strand_normal(ma, ornor, cur_length, (key-1)->vel);
+ }
+
+ if (use_length_check && k > 1) {
+ float dvec[3];
+ /* check if path needs to be cut before actual end of data points */
+ if (!check_path_length(k, keys, key, max_length, step_length, &cur_length, dvec)) {
+ /* last key */
+ sub_v3_v3v3(key->vel, key->co, (key-1)->co);
+ if (ma && draw_col_ma) {
+ copy_v3_v3(key->col, &ma->r);
+ }
+ break;
+ }
+ }
+ if (k == totkeys-1) {
+ /* last key */
+ sub_v3_v3v3(key->vel, key->co, (key-1)->co);
+ }
+
+ if (ma && draw_col_ma) {
+ copy_v3_v3(key->col, &ma->r);
+ get_strand_normal(ma, ornor, cur_length, key->vel);
+ }
+ }
+ }
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+
+void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape,
+ float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start)
+{
+ float kink[3] = {1.f, 0.f, 0.f}, par_vec[3], q1[4] = {1.f, 0.f, 0.f, 0.f};
+ float t, dt = 1.f, result[3];
+
+ if (ELEM(type, PART_KINK_NO, PART_KINK_SPIRAL))
+ return;
+
+ CLAMP(time, 0.f, 1.f);
+
+ if (shape != 0.0f && !ELEM(type, PART_KINK_BRAID)) {
+ if (shape < 0.0f)
+ time = (float)pow(time, 1.f + shape);
+ else
+ time = (float)pow(time, 1.f / (1.f - shape));
+ }
+
+ t = time * freq * (float)M_PI;
+
+ if (smooth_start) {
+ dt = fabsf(t);
+ /* smooth the beginning of kink */
+ CLAMP(dt, 0.f, (float)M_PI);
+ dt = sinf(dt / 2.f);
+ }
+
+ if (!ELEM(type, PART_KINK_RADIAL)) {
+ float temp[3];
+
+ kink[axis] = 1.f;
+
+ if (obmat)
+ mul_mat3_m4_v3(obmat, kink);
+
+ mul_qt_v3(par_rot, kink);
+
+ /* make sure kink is normal to strand */
+ project_v3_v3v3(temp, kink, par_vel);
+ sub_v3_v3(kink, temp);
+ normalize_v3(kink);
+ }
+
+ copy_v3_v3(result, state->co);
+ sub_v3_v3v3(par_vec, par_co, state->co);
+
+ switch (type) {
+ case PART_KINK_CURL:
+ {
+ float curl_offset[3];
+
+ /* rotate kink vector around strand tangent */
+ mul_v3_v3fl(curl_offset, kink, amplitude);
+ axis_angle_to_quat(q1, par_vel, t);
+ mul_qt_v3(q1, curl_offset);
+
+ interp_v3_v3v3(par_vec, state->co, par_co, flat);
+ add_v3_v3v3(result, par_vec, curl_offset);
+ break;
+ }
+ case PART_KINK_RADIAL:
+ {
+ if (flat > 0.f) {
+ float proj[3];
+ /* flatten along strand */
+ project_v3_v3v3(proj, par_vec, par_vel);
+ madd_v3_v3fl(result, proj, flat);
+ }
+
+ madd_v3_v3fl(result, par_vec, -amplitude * sinf(t));
+ break;
+ }
+ case PART_KINK_WAVE:
+ {
+ madd_v3_v3fl(result, kink, amplitude * sinf(t));
+
+ if (flat > 0.f) {
+ float proj[3];
+ /* flatten along wave */
+ project_v3_v3v3(proj, par_vec, kink);
+ madd_v3_v3fl(result, proj, flat);
+
+ /* flatten along strand */
+ project_v3_v3v3(proj, par_vec, par_vel);
+ madd_v3_v3fl(result, proj, flat);
+ }
+ break;
+ }
+ case PART_KINK_BRAID:
+ {
+ float y_vec[3] = {0.f, 1.f, 0.f};
+ float z_vec[3] = {0.f, 0.f, 1.f};
+ float vec_one[3], state_co[3];
+ float inp_y, inp_z, length;
+
+ if (par_rot) {
+ mul_qt_v3(par_rot, y_vec);
+ mul_qt_v3(par_rot, z_vec);
+ }
+
+ negate_v3(par_vec);
+ normalize_v3_v3(vec_one, par_vec);
+
+ inp_y = dot_v3v3(y_vec, vec_one);
+ inp_z = dot_v3v3(z_vec, vec_one);
+
+ if (inp_y > 0.5f) {
+ copy_v3_v3(state_co, y_vec);
+
+ mul_v3_fl(y_vec, amplitude * cosf(t));
+ mul_v3_fl(z_vec, amplitude / 2.f * sinf(2.f * t));
+ }
+ else if (inp_z > 0.0f) {
+ mul_v3_v3fl(state_co, z_vec, sinf((float)M_PI / 3.f));
+ madd_v3_v3fl(state_co, y_vec, -0.5f);
+
+ mul_v3_fl(y_vec, -amplitude * cosf(t + (float)M_PI / 3.f));
+ mul_v3_fl(z_vec, amplitude / 2.f * cosf(2.f * t + (float)M_PI / 6.f));
+ }
+ else {
+ mul_v3_v3fl(state_co, z_vec, -sinf((float)M_PI / 3.f));
+ madd_v3_v3fl(state_co, y_vec, -0.5f);
+
+ mul_v3_fl(y_vec, amplitude * -sinf(t + (float)M_PI / 6.f));
+ mul_v3_fl(z_vec, amplitude / 2.f * -sinf(2.f * t + (float)M_PI / 3.f));
+ }
+
+ mul_v3_fl(state_co, amplitude);
+ add_v3_v3(state_co, par_co);
+ sub_v3_v3v3(par_vec, state->co, state_co);
+
+ length = normalize_v3(par_vec);
+ mul_v3_fl(par_vec, MIN2(length, amplitude / 2.f));
+
+ add_v3_v3v3(state_co, par_co, y_vec);
+ add_v3_v3(state_co, z_vec);
+ add_v3_v3(state_co, par_vec);
+
+ shape = 2.f * (float)M_PI * (1.f + shape);
+
+ if (t < shape) {
+ shape = t / shape;
+ shape = (float)sqrt((double)shape);
+ interp_v3_v3v3(result, result, state_co, shape);
+ }
+ else {
+ copy_v3_v3(result, state_co);
+ }
+ break;
+ }
+ }
+
+ /* blend the start of the kink */
+ if (dt < 1.f)
+ interp_v3_v3v3(state->co, state->co, result, dt);
+ else
+ copy_v3_v3(state->co, result);
+}
+
+static float do_clump_level(float result[3], const float co[3], const float par_co[3], float time,
+ float clumpfac, float clumppow, float pa_clump, CurveMapping *clumpcurve)
+{
+ float clump = 0.0f;
+
+ if (clumpcurve) {
+ clump = pa_clump * (1.0f - CLAMPIS(curvemapping_evaluateF(clumpcurve, 0, time), 0.0f, 1.0f));
+
+ interp_v3_v3v3(result, co, par_co, clump);
+ }
+ else if (clumpfac != 0.0f) {
+ float cpow;
+
+ if (clumppow < 0.0f)
+ cpow = 1.0f + clumppow;
+ else
+ cpow = 1.0f + 9.0f * clumppow;
+
+ if (clumpfac < 0.0f) /* clump roots instead of tips */
+ clump = -clumpfac * pa_clump * (float)pow(1.0 - (double)time, (double)cpow);
+ else
+ clump = clumpfac * pa_clump * (float)pow((double)time, (double)cpow);
+
+ interp_v3_v3v3(result, co, par_co, clump);
+ }
+
+ return clump;
+}
+
+float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
+ bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve)
+{
+ float clump;
+
+ if (use_clump_noise && clump_noise_size != 0.0f) {
+ float center[3], noisevec[3];
+ float da[4], pa[12];
+
+ mul_v3_v3fl(noisevec, orco_offset, 1.0f / clump_noise_size);
+ voronoi(noisevec[0], noisevec[1], noisevec[2], da, pa, 1.0f, 0);
+ mul_v3_fl(&pa[0], clump_noise_size);
+ add_v3_v3v3(center, par_co, &pa[0]);
+
+ do_clump_level(state->co, state->co, center, time, clumpfac, clumppow, pa_clump, clumpcurve);
+ }
+
+ clump = do_clump_level(state->co, state->co, par_co, time, clumpfac, clumppow, pa_clump, clumpcurve);
+
+ return clump;
+}
+
+static void do_rough(const float loc[3], float mat[4][4], float t, float fac, float size, float thres, ParticleKey *state)
+{
+ float rough[3];
+ float rco[3];
+
+ if (thres != 0.0f) {
+ if (fabsf((float)(-1.5f + loc[0] + loc[1] + loc[2])) < 1.5f * thres) {
+ return;
+ }
+ }
+
+ copy_v3_v3(rco, loc);
+ mul_v3_fl(rco, t);
+ rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2);
+ rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2);
+ rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2);
+
+ madd_v3_v3fl(state->co, mat[0], fac * rough[0]);
+ madd_v3_v3fl(state->co, mat[1], fac * rough[1]);
+ madd_v3_v3fl(state->co, mat[2], fac * rough[2]);
+}
+
+static void do_rough_end(const float loc[3], float mat[4][4], float t, float fac, float shape, ParticleKey *state)
+{
+ float rough[2];
+ float roughfac;
+
+ roughfac = fac * (float)pow((double)t, shape);
+ copy_v2_v2(rough, loc);
+ rough[0] = -1.0f + 2.0f * rough[0];
+ rough[1] = -1.0f + 2.0f * rough[1];
+ mul_v2_fl(rough, roughfac);
+
+ madd_v3_v3fl(state->co, mat[0], rough[0]);
+ madd_v3_v3fl(state->co, mat[1], rough[1]);
+}
+
+static void do_rough_curve(const float loc[3], float mat[4][4], float time, float fac, float size, CurveMapping *roughcurve, ParticleKey *state)
+{
+ float rough[3];
+ float rco[3];
+
+ if (!roughcurve)
+ return;
+
+ fac *= CLAMPIS(curvemapping_evaluateF(roughcurve, 0, time), 0.0f, 1.0f);
+
+ copy_v3_v3(rco, loc);
+ mul_v3_fl(rco, time);
+ rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2);
+ rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2);
+ rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2);
+
+ madd_v3_v3fl(state->co, mat[0], fac * rough[0]);
+ madd_v3_v3fl(state->co, mat[1], fac * rough[1]);
+ madd_v3_v3fl(state->co, mat[2], fac * rough[2]);
+}
+
+void do_child_modifiers(ParticleThreadContext *ctx, ParticleSimulationData *sim, ParticleTexture *ptex,
+ const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3],
+ ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t)
+{
+ ParticleSettings *part = sim->psys->part;
+ CurveMapping *clumpcurve = NULL, *roughcurve = NULL;
+ int i = cpa - sim->psys->child;
+ int guided = 0;
+
+ if (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) {
+ clumpcurve = (ctx != NULL) ? ctx->clumpcurve : part->clumpcurve;
+ }
+ if (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) {
+ roughcurve = (ctx != NULL) ? ctx->roughcurve : part->roughcurve;
+ }
+
+ float kink_amp = part->kink_amp;
+ float kink_amp_clump = part->kink_amp_clump;
+ float kink_freq = part->kink_freq;
+ float rough1 = part->rough1;
+ float rough2 = part->rough2;
+ float rough_end = part->rough_end;
+ const bool smooth_start = (sim->psys->part->childtype == PART_CHILD_FACES);
+
+ if (ptex) {
+ kink_amp *= ptex->kink_amp;
+ kink_freq *= ptex->kink_freq;
+ rough1 *= ptex->rough1;
+ rough2 *= ptex->rough2;
+ rough_end *= ptex->roughe;
+ }
+
+ if (part->flag & PART_CHILD_EFFECT)
+ /* state is safe to cast, since only co and vel are used */
+ guided = do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)state, cpa->parent, t);
+
+ if (guided == 0) {
+ float orco_offset[3];
+ float clump;
+
+ sub_v3_v3v3(orco_offset, orco, par_orco);
+ clump = do_clump(state, par_co, t, orco_offset, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f,
+ part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve);
+
+ if (kink_freq != 0.f) {
+ kink_amp *= (1.f - kink_amp_clump * clump);
+
+ do_kink(state, par_co, par_vel, par_rot, t, kink_freq, part->kink_shape,
+ kink_amp, part->kink_flat, part->kink, part->kink_axis,
+ sim->ob->obmat, smooth_start);
+ }
+ }
+
+ if (roughcurve) {
+ do_rough_curve(orco, mat, t, rough1, part->rough1_size, roughcurve, state);
+ }
+ else {
+ if (rough1 > 0.f)
+ do_rough(orco, mat, t, rough1, part->rough1_size, 0.0, state);
+
+ if (rough2 > 0.f) {
+ float vec[3];
+ psys_frand_vec(sim->psys, i + 27, vec);
+ do_rough(vec, mat, t, rough2, part->rough2_size, part->rough2_thres, state);
+ }
+
+ if (rough_end > 0.f) {
+ float vec[3];
+ psys_frand_vec(sim->psys, i + 27, vec);
+ do_rough_end(vec, mat, t, rough_end, part->rough_end_shape, state);
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
new file mode 100644
index 00000000000..44cf5b119c1
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -0,0 +1,1476 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Raul Fernandez Hernandez (Farsthary),
+ * Stephen Swhitehorn,
+ * Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/particle_distribute.c
+ * \ingroup bke
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_jitter.h"
+#include "BLI_kdtree.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_sort.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+
+static int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot);
+
+static void alloc_child_particles(ParticleSystem *psys, int tot)
+{
+ if (psys->child) {
+ /* only re-allocate if we have to */
+ if (psys->part->childtype && psys->totchild == tot) {
+ memset(psys->child, 0, tot*sizeof(ChildParticle));
+ return;
+ }
+
+ MEM_freeN(psys->child);
+ psys->child=NULL;
+ psys->totchild=0;
+ }
+
+ if (psys->part->childtype) {
+ psys->totchild= tot;
+ if (psys->totchild)
+ psys->child= MEM_callocN(psys->totchild*sizeof(ChildParticle), "child_particles");
+ }
+}
+
+static void distribute_simple_children(Scene *scene, Object *ob, DerivedMesh *finaldm, DerivedMesh *deformdm, ParticleSystem *psys)
+{
+ ChildParticle *cpa = NULL;
+ int i, p;
+ int child_nbr= psys_get_child_number(scene, psys);
+ int totpart= psys_get_tot_child(scene, psys);
+
+ alloc_child_particles(psys, totpart);
+
+ cpa = psys->child;
+ for (i=0; i<child_nbr; i++) {
+ for (p=0; p<psys->totpart; p++,cpa++) {
+ float length=2.0;
+ cpa->parent=p;
+
+ /* create even spherical distribution inside unit sphere */
+ while (length>=1.0f) {
+ cpa->fuv[0]=2.0f*BLI_frand()-1.0f;
+ cpa->fuv[1]=2.0f*BLI_frand()-1.0f;
+ cpa->fuv[2]=2.0f*BLI_frand()-1.0f;
+ length=len_v3(cpa->fuv);
+ }
+
+ cpa->num=-1;
+ }
+ }
+ /* dmcache must be updated for parent particles if children from faces is used */
+ psys_calc_dmcache(ob, finaldm, deformdm, psys);
+}
+static void distribute_grid(DerivedMesh *dm, ParticleSystem *psys)
+{
+ ParticleData *pa=NULL;
+ float min[3], max[3], delta[3], d;
+ MVert *mv, *mvert = dm->getVertDataArray(dm,0);
+ int totvert=dm->getNumVerts(dm), from=psys->part->from;
+ int i, j, k, p, res=psys->part->grid_res, size[3], axis;
+
+ /* find bounding box of dm */
+ if (totvert > 0) {
+ mv=mvert;
+ copy_v3_v3(min, mv->co);
+ copy_v3_v3(max, mv->co);
+ mv++;
+ for (i = 1; i < totvert; i++, mv++) {
+ minmax_v3v3_v3(min, max, mv->co);
+ }
+ }
+ else {
+ zero_v3(min);
+ zero_v3(max);
+ }
+
+ sub_v3_v3v3(delta, max, min);
+
+ /* determine major axis */
+ axis = axis_dominant_v3_single(delta);
+
+ d = delta[axis]/(float)res;
+
+ size[axis] = res;
+ size[(axis+1)%3] = (int)ceil(delta[(axis+1)%3]/d);
+ size[(axis+2)%3] = (int)ceil(delta[(axis+2)%3]/d);
+
+ /* float errors grrr.. */
+ size[(axis+1)%3] = MIN2(size[(axis+1)%3],res);
+ size[(axis+2)%3] = MIN2(size[(axis+2)%3],res);
+
+ size[0] = MAX2(size[0], 1);
+ size[1] = MAX2(size[1], 1);
+ size[2] = MAX2(size[2], 1);
+
+ /* no full offset for flat/thin objects */
+ min[0]+= d < delta[0] ? d/2.f : delta[0]/2.f;
+ min[1]+= d < delta[1] ? d/2.f : delta[1]/2.f;
+ min[2]+= d < delta[2] ? d/2.f : delta[2]/2.f;
+
+ for (i=0,p=0,pa=psys->particles; i<res; i++) {
+ for (j=0; j<res; j++) {
+ for (k=0; k<res; k++,p++,pa++) {
+ pa->fuv[0] = min[0] + (float)i*d;
+ pa->fuv[1] = min[1] + (float)j*d;
+ pa->fuv[2] = min[2] + (float)k*d;
+ pa->flag |= PARS_UNEXIST;
+ pa->hair_index = 0; /* abused in volume calculation */
+ }
+ }
+ }
+
+ /* enable particles near verts/edges/faces/inside surface */
+ if (from==PART_FROM_VERT) {
+ float vec[3];
+
+ pa=psys->particles;
+
+ min[0] -= d/2.0f;
+ min[1] -= d/2.0f;
+ min[2] -= d/2.0f;
+
+ for (i=0,mv=mvert; i<totvert; i++,mv++) {
+ sub_v3_v3v3(vec,mv->co,min);
+ vec[0]/=delta[0];
+ vec[1]/=delta[1];
+ vec[2]/=delta[2];
+ pa[((int)(vec[0] * (size[0] - 1)) * res +
+ (int)(vec[1] * (size[1] - 1))) * res +
+ (int)(vec[2] * (size[2] - 1))].flag &= ~PARS_UNEXIST;
+ }
+ }
+ else if (ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) {
+ float co1[3], co2[3];
+
+ MFace *mface= NULL, *mface_array;
+ float v1[3], v2[3], v3[3], v4[4], lambda;
+ int a, a1, a2, a0mul, a1mul, a2mul, totface;
+ int amax= from==PART_FROM_FACE ? 3 : 1;
+
+ totface=dm->getNumTessFaces(dm);
+ mface=mface_array=dm->getTessFaceDataArray(dm,CD_MFACE);
+
+ for (a=0; a<amax; a++) {
+ if (a==0) { a0mul=res*res; a1mul=res; a2mul=1; }
+ else if (a==1) { a0mul=res; a1mul=1; a2mul=res*res; }
+ else { a0mul=1; a1mul=res*res; a2mul=res; }
+
+ for (a1=0; a1<size[(a+1)%3]; a1++) {
+ for (a2=0; a2<size[(a+2)%3]; a2++) {
+ mface= mface_array;
+
+ pa = psys->particles + a1*a1mul + a2*a2mul;
+ copy_v3_v3(co1, pa->fuv);
+ co1[a] -= d < delta[a] ? d/2.f : delta[a]/2.f;
+ copy_v3_v3(co2, co1);
+ co2[a] += delta[a] + 0.001f*d;
+ co1[a] -= 0.001f*d;
+
+ /* lets intersect the faces */
+ for (i=0; i<totface; i++,mface++) {
+ copy_v3_v3(v1, mvert[mface->v1].co);
+ copy_v3_v3(v2, mvert[mface->v2].co);
+ copy_v3_v3(v3, mvert[mface->v3].co);
+
+ bool intersects_tri = isect_axial_line_segment_tri_v3(a, co1, co2, v2, v3, v1, &lambda);
+ if (intersects_tri) {
+ if (from==PART_FROM_FACE)
+ (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST;
+ else /* store number of intersections */
+ (pa+(int)(lambda*size[a])*a0mul)->hair_index++;
+ }
+
+ if (mface->v4 && (!intersects_tri || from==PART_FROM_VOLUME)) {
+ copy_v3_v3(v4, mvert[mface->v4].co);
+
+ if (isect_axial_line_segment_tri_v3(a, co1, co2, v4, v1, v3, &lambda)) {
+ if (from==PART_FROM_FACE)
+ (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST;
+ else
+ (pa+(int)(lambda*size[a])*a0mul)->hair_index++;
+ }
+ }
+ }
+
+ if (from==PART_FROM_VOLUME) {
+ int in=pa->hair_index%2;
+ if (in) pa->hair_index++;
+ for (i=0; i<size[0]; i++) {
+ if (in || (pa+i*a0mul)->hair_index%2)
+ (pa+i*a0mul)->flag &= ~PARS_UNEXIST;
+ /* odd intersections == in->out / out->in */
+ /* even intersections -> in stays same */
+ in=(in + (pa+i*a0mul)->hair_index) % 2;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (psys->part->flag & PART_GRID_HEXAGONAL) {
+ for (i=0,p=0,pa=psys->particles; i<res; i++) {
+ for (j=0; j<res; j++) {
+ for (k=0; k<res; k++,p++,pa++) {
+ if (j%2)
+ pa->fuv[0] += d/2.f;
+
+ if (k%2) {
+ pa->fuv[0] += d/2.f;
+ pa->fuv[1] += d/2.f;
+ }
+ }
+ }
+ }
+ }
+
+ if (psys->part->flag & PART_GRID_INVERT) {
+ for (i=0; i<size[0]; i++) {
+ for (j=0; j<size[1]; j++) {
+ pa=psys->particles + res*(i*res + j);
+ for (k=0; k<size[2]; k++, pa++) {
+ pa->flag ^= PARS_UNEXIST;
+ }
+ }
+ }
+ }
+
+ if (psys->part->grid_rand > 0.f) {
+ float rfac = d * psys->part->grid_rand;
+ for (p=0,pa=psys->particles; p<psys->totpart; p++,pa++) {
+ if (pa->flag & PARS_UNEXIST)
+ continue;
+
+ pa->fuv[0] += rfac * (psys_frand(psys, p + 31) - 0.5f);
+ pa->fuv[1] += rfac * (psys_frand(psys, p + 32) - 0.5f);
+ pa->fuv[2] += rfac * (psys_frand(psys, p + 33) - 0.5f);
+ }
+ }
+}
+
+/* modified copy from rayshade.c */
+static void hammersley_create(float *out, int n, int seed, float amount)
+{
+ RNG *rng;
+ double p, t, offs[2];
+ int k, kk;
+
+ rng = BLI_rng_new(31415926 + n + seed);
+ offs[0] = BLI_rng_get_double(rng) + (double)amount;
+ offs[1] = BLI_rng_get_double(rng) + (double)amount;
+ BLI_rng_free(rng);
+
+ for (k = 0; k < n; k++) {
+ t = 0;
+ for (p = 0.5, kk = k; kk; p *= 0.5, kk >>= 1)
+ if (kk & 1) /* kk mod 2 = 1 */
+ t += p;
+
+ out[2*k + 0] = fmod((double)k/(double)n + offs[0], 1.0);
+ out[2*k + 1] = fmod(t + offs[1], 1.0);
+ }
+}
+
+/* almost exact copy of BLI_jitter_init */
+static void init_mv_jit(float *jit, int num, int seed2, float amount)
+{
+ RNG *rng;
+ float *jit2, x, rad1, rad2, rad3;
+ int i, num2;
+
+ if (num==0) return;
+
+ rad1= (float)(1.0f/sqrtf((float)num));
+ rad2= (float)(1.0f/((float)num));
+ rad3= (float)sqrtf((float)num)/((float)num);
+
+ rng = BLI_rng_new(31415926 + num + seed2);
+ x= 0;
+ num2 = 2 * num;
+ for (i=0; i<num2; i+=2) {
+
+ jit[i] = x + amount*rad1*(0.5f - BLI_rng_get_float(rng));
+ jit[i+1] = i/(2.0f*num) + amount*rad1*(0.5f - BLI_rng_get_float(rng));
+
+ jit[i]-= (float)floor(jit[i]);
+ jit[i+1]-= (float)floor(jit[i+1]);
+
+ x+= rad3;
+ x -= (float)floor(x);
+ }
+
+ jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit");
+
+ for (i=0 ; i<4 ; i++) {
+ BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1);
+ BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1);
+ BLI_jitterate2((float (*)[2])jit, (float (*)[2])jit2, num, rad2);
+ }
+ MEM_freeN(jit2);
+ BLI_rng_free(rng);
+}
+
+static void psys_uv_to_w(float u, float v, int quad, float *w)
+{
+ float vert[4][3], co[3];
+
+ if (!quad) {
+ if (u+v > 1.0f)
+ v= 1.0f-v;
+ else
+ u= 1.0f-u;
+ }
+
+ vert[0][0] = 0.0f; vert[0][1] = 0.0f; vert[0][2] = 0.0f;
+ vert[1][0] = 1.0f; vert[1][1] = 0.0f; vert[1][2] = 0.0f;
+ vert[2][0] = 1.0f; vert[2][1] = 1.0f; vert[2][2] = 0.0f;
+
+ co[0] = u;
+ co[1] = v;
+ co[2] = 0.0f;
+
+ if (quad) {
+ vert[3][0] = 0.0f; vert[3][1] = 1.0f; vert[3][2] = 0.0f;
+ interp_weights_poly_v3( w,vert, 4, co);
+ }
+ else {
+ interp_weights_poly_v3( w,vert, 3, co);
+ w[3] = 0.0f;
+ }
+}
+
+/* Find the index in "sum" array before "value" is crossed. */
+static int distribute_binary_search(float *sum, int n, float value)
+{
+ int mid, low = 0, high = n - 1;
+
+ if (high == low)
+ return low;
+
+ if (sum[low] >= value)
+ return low;
+
+ if (sum[high - 1] < value)
+ return high;
+
+ while (low < high) {
+ mid = (low + high) / 2;
+
+ if ((sum[mid] >= value) && (sum[mid - 1] < value))
+ return mid;
+
+ if (sum[mid] > value) {
+ high = mid - 1;
+ }
+ else {
+ low = mid + 1;
+ }
+ }
+
+ return low;
+}
+
+/* the max number if calls to rng_* funcs within psys_thread_distribute_particle
+ * be sure to keep up to date if this changes */
+#define PSYS_RND_DIST_SKIP 2
+
+/* note: this function must be thread safe, for from == PART_FROM_CHILD */
+#define ONLY_WORKING_WITH_PA_VERTS 0
+static void distribute_from_verts_exec(ParticleTask *thread, ParticleData *pa, int p)
+{
+ ParticleThreadContext *ctx= thread->ctx;
+ int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */
+
+ /* TODO_PARTICLE - use original index */
+ pa->num= ctx->index[p];
+ pa->fuv[0] = 1.0f;
+ pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0;
+
+#if ONLY_WORKING_WITH_PA_VERTS
+ if (ctx->tree) {
+ KDTreeNearest ptn[3];
+ int w, maxw;
+
+ psys_particle_on_dm(ctx->dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,orco1,0);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1);
+ maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3);
+
+ for (w=0; w<maxw; w++) {
+ pa->verts[w]=ptn->num;
+ }
+ }
+#endif
+
+ if (rng_skip_tot > 0) /* should never be below zero */
+ BLI_rng_skip(thread->rng, rng_skip_tot);
+}
+
+static void distribute_from_faces_exec(ParticleTask *thread, ParticleData *pa, int p) {
+ ParticleThreadContext *ctx= thread->ctx;
+ DerivedMesh *dm= ctx->dm;
+ float randu, randv;
+ int distr= ctx->distr;
+ int i;
+ int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */
+
+ MFace *mface;
+
+ pa->num = i = ctx->index[p];
+ mface = dm->getTessFaceData(dm,i,CD_MFACE);
+
+ switch (distr) {
+ case PART_DISTR_JIT:
+ if (ctx->jitlevel == 1) {
+ if (mface->v4)
+ psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv);
+ else
+ psys_uv_to_w(1.0f / 3.0f, 1.0f / 3.0f, mface->v4, pa->fuv);
+ }
+ else {
+ float offset = fmod(ctx->jitoff[i] + (float)p, (float)ctx->jitlevel);
+ if (!isnan(offset)) {
+ psys_uv_to_w(ctx->jit[2*(int)offset], ctx->jit[2*(int)offset+1], mface->v4, pa->fuv);
+ }
+ }
+ break;
+ case PART_DISTR_RAND:
+ randu= BLI_rng_get_float(thread->rng);
+ randv= BLI_rng_get_float(thread->rng);
+ rng_skip_tot -= 2;
+
+ psys_uv_to_w(randu, randv, mface->v4, pa->fuv);
+ break;
+ }
+ pa->foffset= 0.0f;
+
+ if (rng_skip_tot > 0) /* should never be below zero */
+ BLI_rng_skip(thread->rng, rng_skip_tot);
+}
+
+static void distribute_from_volume_exec(ParticleTask *thread, ParticleData *pa, int p) {
+ ParticleThreadContext *ctx= thread->ctx;
+ DerivedMesh *dm= ctx->dm;
+ float *v1, *v2, *v3, *v4, nor[3], co[3];
+ float cur_d, min_d, randu, randv;
+ int distr= ctx->distr;
+ int i, intersect, tot;
+ int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */
+
+ MFace *mface;
+ MVert *mvert=dm->getVertDataArray(dm,CD_MVERT);
+
+ pa->num = i = ctx->index[p];
+ mface = dm->getTessFaceData(dm,i,CD_MFACE);
+
+ switch (distr) {
+ case PART_DISTR_JIT:
+ if (ctx->jitlevel == 1) {
+ if (mface->v4)
+ psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv);
+ else
+ psys_uv_to_w(1.0f / 3.0f, 1.0f / 3.0f, mface->v4, pa->fuv);
+ }
+ else {
+ float offset = fmod(ctx->jitoff[i] + (float)p, (float)ctx->jitlevel);
+ if (!isnan(offset)) {
+ psys_uv_to_w(ctx->jit[2*(int)offset], ctx->jit[2*(int)offset+1], mface->v4, pa->fuv);
+ }
+ }
+ break;
+ case PART_DISTR_RAND:
+ randu= BLI_rng_get_float(thread->rng);
+ randv= BLI_rng_get_float(thread->rng);
+ rng_skip_tot -= 2;
+
+ psys_uv_to_w(randu, randv, mface->v4, pa->fuv);
+ break;
+ }
+ pa->foffset= 0.0f;
+
+ /* experimental */
+ tot=dm->getNumTessFaces(dm);
+
+ psys_interpolate_face(mvert,mface,0,0,pa->fuv,co,nor,0,0,0,0);
+
+ normalize_v3(nor);
+ negate_v3(nor);
+
+ min_d=FLT_MAX;
+ intersect=0;
+
+ for (i=0,mface=dm->getTessFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++) {
+ if (i==pa->num) continue;
+
+ v1=mvert[mface->v1].co;
+ v2=mvert[mface->v2].co;
+ v3=mvert[mface->v3].co;
+
+ if (isect_ray_tri_v3(co, nor, v2, v3, v1, &cur_d, NULL)) {
+ if (cur_d<min_d) {
+ min_d=cur_d;
+ pa->foffset=cur_d*0.5f; /* to the middle of volume */
+ intersect=1;
+ }
+ }
+ if (mface->v4) {
+ v4=mvert[mface->v4].co;
+
+ if (isect_ray_tri_v3(co, nor, v4, v1, v3, &cur_d, NULL)) {
+ if (cur_d<min_d) {
+ min_d=cur_d;
+ pa->foffset=cur_d*0.5f; /* to the middle of volume */
+ intersect=1;
+ }
+ }
+ }
+ }
+ if (intersect==0)
+ pa->foffset=0.0;
+ else {
+ switch (distr) {
+ case PART_DISTR_JIT:
+ pa->foffset *= ctx->jit[p % (2 * ctx->jitlevel)];
+ break;
+ case PART_DISTR_RAND:
+ pa->foffset *= BLI_frand();
+ break;
+ }
+ }
+
+ if (rng_skip_tot > 0) /* should never be below zero */
+ BLI_rng_skip(thread->rng, rng_skip_tot);
+}
+
+static void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, int p) {
+ ParticleThreadContext *ctx= thread->ctx;
+ Object *ob= ctx->sim.ob;
+ DerivedMesh *dm= ctx->dm;
+ float orco1[3], co1[3], nor1[3];
+ float randu, randv;
+ int cfrom= ctx->cfrom;
+ int i;
+ int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */
+
+ MFace *mf;
+
+ if (ctx->index[p] < 0) {
+ cpa->num=0;
+ cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f;
+ cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0;
+ return;
+ }
+
+ mf= dm->getTessFaceData(dm, ctx->index[p], CD_MFACE);
+
+ randu= BLI_rng_get_float(thread->rng);
+ randv= BLI_rng_get_float(thread->rng);
+ rng_skip_tot -= 2;
+
+ psys_uv_to_w(randu, randv, mf->v4, cpa->fuv);
+
+ cpa->num = ctx->index[p];
+
+ if (ctx->tree) {
+ KDTreeNearest ptn[10];
+ int w,maxw;//, do_seams;
+ float maxd /*, mind,dd */, totw= 0.0f;
+ int parent[10];
+ float pweight[10];
+
+ psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,NULL,NULL,orco1,NULL);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1);
+ maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3);
+
+ maxd=ptn[maxw-1].dist;
+ /* mind=ptn[0].dist; */ /* UNUSED */
+
+ /* the weights here could be done better */
+ for (w=0; w<maxw; w++) {
+ parent[w]=ptn[w].index;
+ pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd));
+ }
+ for (;w<10; w++) {
+ parent[w]=-1;
+ pweight[w]=0.0f;
+ }
+
+ for (w=0,i=0; w<maxw && i<4; w++) {
+ if (parent[w]>=0) {
+ cpa->pa[i]=parent[w];
+ cpa->w[i]=pweight[w];
+ totw+=pweight[w];
+ i++;
+ }
+ }
+ for (;i<4; i++) {
+ cpa->pa[i]=-1;
+ cpa->w[i]=0.0f;
+ }
+
+ if (totw > 0.0f) {
+ for (w = 0; w < 4; w++) {
+ cpa->w[w] /= totw;
+ }
+ }
+
+ cpa->parent=cpa->pa[0];
+ }
+
+ if (rng_skip_tot > 0) /* should never be below zero */
+ BLI_rng_skip(thread->rng, rng_skip_tot);
+}
+
+static void exec_distribute_parent(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid))
+{
+ ParticleTask *task = taskdata;
+ ParticleSystem *psys= task->ctx->sim.psys;
+ ParticleData *pa;
+ int p;
+
+ BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->begin);
+
+ pa= psys->particles + task->begin;
+ switch (psys->part->from) {
+ case PART_FROM_FACE:
+ for (p = task->begin; p < task->end; ++p, ++pa)
+ distribute_from_faces_exec(task, pa, p);
+ break;
+ case PART_FROM_VOLUME:
+ for (p = task->begin; p < task->end; ++p, ++pa)
+ distribute_from_volume_exec(task, pa, p);
+ break;
+ case PART_FROM_VERT:
+ for (p = task->begin; p < task->end; ++p, ++pa)
+ distribute_from_verts_exec(task, pa, p);
+ break;
+ }
+}
+
+static void exec_distribute_child(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid))
+{
+ ParticleTask *task = taskdata;
+ ParticleSystem *psys = task->ctx->sim.psys;
+ ChildParticle *cpa;
+ int p;
+
+ /* RNG skipping at the beginning */
+ cpa = psys->child;
+ for (p = 0; p < task->begin; ++p, ++cpa) {
+ if (task->ctx->skip) /* simplification skip */
+ BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->ctx->skip[p]);
+
+ BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP);
+ }
+
+ for (; p < task->end; ++p, ++cpa) {
+ if (task->ctx->skip) /* simplification skip */
+ BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->ctx->skip[p]);
+
+ distribute_children_exec(task, cpa, p);
+ }
+}
+
+static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data)
+{
+ int *orig_index = (int *) user_data;
+ int index1 = orig_index[*(const int *)p1];
+ int index2 = orig_index[*(const int *)p2];
+
+ if (index1 < index2)
+ return -1;
+ else if (index1 == index2) {
+ /* this pointer comparison appears to make qsort stable for glibc,
+ * and apparently on solaris too, makes the renders reproducible */
+ if (p1 < p2)
+ return -1;
+ else if (p1 == p2)
+ return 0;
+ else
+ return 1;
+ }
+ else
+ return 1;
+}
+
+static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from)
+{
+ if (from == PART_FROM_CHILD) {
+ ChildParticle *cpa;
+ int p, totchild = psys_get_tot_child(scene, psys);
+
+ if (psys->child && totchild) {
+ for (p=0,cpa=psys->child; p<totchild; p++,cpa++) {
+ cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3] = 0.0;
+ cpa->foffset= 0.0f;
+ cpa->parent=0;
+ cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0;
+ cpa->num= -1;
+ }
+ }
+ }
+ else {
+ PARTICLE_P;
+ LOOP_PARTICLES {
+ pa->fuv[0] = pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0;
+ pa->foffset= 0.0f;
+ pa->num= -1;
+ }
+ }
+}
+
+/* Creates a distribution of coordinates on a DerivedMesh */
+/* This is to denote functionality that does not yet work with mesh - only derived mesh */
+static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, ParticleSimulationData *sim, int from)
+{
+ Scene *scene = sim->scene;
+ DerivedMesh *finaldm = sim->psmd->dm_final;
+ Object *ob = sim->ob;
+ ParticleSystem *psys= sim->psys;
+ ParticleData *pa=0, *tpars= 0;
+ ParticleSettings *part;
+ ParticleSeam *seams= 0;
+ KDTree *tree=0;
+ DerivedMesh *dm= NULL;
+ float *jit= NULL;
+ int i, p=0;
+ int cfrom=0;
+ int totelem=0, totpart, *particle_element=0, children=0, totseam=0;
+ int jitlevel= 1, distr;
+ float *element_weight=NULL,*jitter_offset=NULL, *vweight=NULL;
+ float cur, maxweight=0.0, tweight, totweight, inv_totweight, co[3], nor[3], orco[3];
+
+ if (ELEM(NULL, ob, psys, psys->part))
+ return 0;
+
+ part=psys->part;
+ totpart=psys->totpart;
+ if (totpart==0)
+ return 0;
+
+ if (!finaldm->deformedOnly && !finaldm->getTessFaceDataArray(finaldm, CD_ORIGINDEX)) {
+ printf("Can't create particles with the current modifier stack, disable destructive modifiers\n");
+// XXX error("Can't paint with the current modifier stack, disable destructive modifiers");
+ return 0;
+ }
+
+ /* XXX This distribution code is totally broken in case from == PART_FROM_CHILD, it's always using finaldm
+ * even if use_modifier_stack is unset... But making things consistent here break all existing edited
+ * hair systems, so better wait for complete rewrite.
+ */
+
+ psys_thread_context_init(ctx, sim);
+
+ /* First handle special cases */
+ if (from == PART_FROM_CHILD) {
+ /* Simple children */
+ if (part->childtype != PART_CHILD_FACES) {
+ BLI_srandom(31415926 + psys->seed + psys->child_seed);
+ distribute_simple_children(scene, ob, finaldm, sim->psmd->dm_deformed, psys);
+ return 0;
+ }
+ }
+ else {
+ /* Grid distribution */
+ if (part->distr==PART_DISTR_GRID && from != PART_FROM_VERT) {
+ BLI_srandom(31415926 + psys->seed);
+
+ if (psys->part->use_modifier_stack) {
+ dm = finaldm;
+ }
+ else {
+ dm = CDDM_from_mesh((Mesh*)ob->data);
+ }
+ DM_ensure_tessface(dm);
+
+ distribute_grid(dm,psys);
+
+ if (dm != finaldm) {
+ dm->release(dm);
+ }
+
+ return 0;
+ }
+ }
+
+ /* Create trees and original coordinates if needed */
+ if (from == PART_FROM_CHILD) {
+ distr=PART_DISTR_RAND;
+ BLI_srandom(31415926 + psys->seed + psys->child_seed);
+ dm= finaldm;
+
+ /* BMESH ONLY */
+ DM_ensure_tessface(dm);
+
+ children=1;
+
+ tree=BLI_kdtree_new(totpart);
+
+ for (p=0,pa=psys->particles; p<totpart; p++,pa++) {
+ psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,NULL);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco, 1, 1);
+ BLI_kdtree_insert(tree, p, orco);
+ }
+
+ BLI_kdtree_balance(tree);
+
+ totpart = psys_get_tot_child(scene, psys);
+ cfrom = from = PART_FROM_FACE;
+ }
+ else {
+ distr = part->distr;
+ BLI_srandom(31415926 + psys->seed);
+
+ if (psys->part->use_modifier_stack)
+ dm = finaldm;
+ else
+ dm= CDDM_from_mesh((Mesh*)ob->data);
+
+ /* BMESH ONLY, for verts we don't care about tessfaces */
+ if (from != PART_FROM_VERT) {
+ DM_ensure_tessface(dm);
+ }
+
+ /* we need orco for consistent distributions */
+ if (!CustomData_has_layer(&dm->vertData, CD_ORCO))
+ DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, BKE_mesh_orco_verts_get(ob));
+
+ if (from == PART_FROM_VERT) {
+ MVert *mv= dm->getVertDataArray(dm, CD_MVERT);
+ float (*orcodata)[3] = dm->getVertDataArray(dm, CD_ORCO);
+ int totvert = dm->getNumVerts(dm);
+
+ tree=BLI_kdtree_new(totvert);
+
+ for (p=0; p<totvert; p++) {
+ if (orcodata) {
+ copy_v3_v3(co,orcodata[p]);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co, 1, 1);
+ }
+ else
+ copy_v3_v3(co,mv[p].co);
+ BLI_kdtree_insert(tree, p, co);
+ }
+
+ BLI_kdtree_balance(tree);
+ }
+ }
+
+ /* Get total number of emission elements and allocate needed arrays */
+ totelem = (from == PART_FROM_VERT) ? dm->getNumVerts(dm) : dm->getNumTessFaces(dm);
+
+ if (totelem == 0) {
+ distribute_invalid(scene, psys, children ? PART_FROM_CHILD : 0);
+
+ if (G.debug & G_DEBUG)
+ fprintf(stderr,"Particle distribution error: Nothing to emit from!\n");
+
+ if (dm != finaldm) dm->release(dm);
+
+ BLI_kdtree_free(tree);
+
+ return 0;
+ }
+
+ element_weight = MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights");
+ particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes");
+ jitter_offset = MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff");
+
+ /* Calculate weights from face areas */
+ if ((part->flag&PART_EDISTR || children) && from != PART_FROM_VERT) {
+ MVert *v1, *v2, *v3, *v4;
+ float totarea=0.f, co1[3], co2[3], co3[3], co4[3];
+ float (*orcodata)[3];
+
+ orcodata= dm->getVertDataArray(dm, CD_ORCO);
+
+ for (i=0; i<totelem; i++) {
+ MFace *mf=dm->getTessFaceData(dm,i,CD_MFACE);
+
+ if (orcodata) {
+ copy_v3_v3(co1, orcodata[mf->v1]);
+ copy_v3_v3(co2, orcodata[mf->v2]);
+ copy_v3_v3(co3, orcodata[mf->v3]);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co1, 1, 1);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co2, 1, 1);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co3, 1, 1);
+ if (mf->v4) {
+ copy_v3_v3(co4, orcodata[mf->v4]);
+ BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co4, 1, 1);
+ }
+ }
+ else {
+ v1= (MVert*)dm->getVertData(dm,mf->v1,CD_MVERT);
+ v2= (MVert*)dm->getVertData(dm,mf->v2,CD_MVERT);
+ v3= (MVert*)dm->getVertData(dm,mf->v3,CD_MVERT);
+ copy_v3_v3(co1, v1->co);
+ copy_v3_v3(co2, v2->co);
+ copy_v3_v3(co3, v3->co);
+ if (mf->v4) {
+ v4= (MVert*)dm->getVertData(dm,mf->v4,CD_MVERT);
+ copy_v3_v3(co4, v4->co);
+ }
+ }
+
+ cur = mf->v4 ? area_quad_v3(co1, co2, co3, co4) : area_tri_v3(co1, co2, co3);
+
+ if (cur > maxweight)
+ maxweight = cur;
+
+ element_weight[i] = cur;
+ totarea += cur;
+ }
+
+ for (i=0; i<totelem; i++)
+ element_weight[i] /= totarea;
+
+ maxweight /= totarea;
+ }
+ else {
+ float min=1.0f/(float)(MIN2(totelem,totpart));
+ for (i=0; i<totelem; i++)
+ element_weight[i]=min;
+ maxweight=min;
+ }
+
+ /* Calculate weights from vgroup */
+ vweight = psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY);
+
+ if (vweight) {
+ if (from==PART_FROM_VERT) {
+ for (i=0;i<totelem; i++)
+ element_weight[i]*=vweight[i];
+ }
+ else { /* PART_FROM_FACE / PART_FROM_VOLUME */
+ for (i=0;i<totelem; i++) {
+ MFace *mf=dm->getTessFaceData(dm,i,CD_MFACE);
+ tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3];
+
+ if (mf->v4) {
+ tweight += vweight[mf->v4];
+ tweight /= 4.0f;
+ }
+ else {
+ tweight /= 3.0f;
+ }
+
+ element_weight[i]*=tweight;
+ }
+ }
+ MEM_freeN(vweight);
+ }
+
+ /* Calculate total weight of all elements */
+ int totmapped = 0;
+ totweight = 0.0f;
+ for (i = 0; i < totelem; i++) {
+ if (element_weight[i] > 0.0f) {
+ totmapped++;
+ totweight += element_weight[i];
+ }
+ }
+
+ if (totmapped == 0) {
+ /* We are not allowed to distribute particles anywhere... */
+ return 0;
+ }
+
+ inv_totweight = 1.0f / totweight;
+
+ /* Calculate cumulative weights.
+ * We remove all null-weighted elements from element_sum, and create a new mapping
+ * 'activ'_elem_index -> orig_elem_index.
+ * This simplifies greatly the filtering of zero-weighted items - and can be much more efficient
+ * especially in random case (reducing a lot the size of binary-searched array)...
+ */
+ float *element_sum = MEM_mallocN(sizeof(*element_sum) * totmapped, __func__);
+ int *element_map = MEM_mallocN(sizeof(*element_map) * totmapped, __func__);
+ int i_mapped = 0;
+
+ for (i = 0; i < totelem && element_weight[i] == 0.0f; i++);
+ element_sum[i_mapped] = element_weight[i] * inv_totweight;
+ element_map[i_mapped] = i;
+ i_mapped++;
+ for (i++; i < totelem; i++) {
+ if (element_weight[i] > 0.0f) {
+ element_sum[i_mapped] = element_sum[i_mapped - 1] + element_weight[i] * inv_totweight;
+ /* Skip elements which weight is so small that it does not affect the sum. */
+ if (element_sum[i_mapped] > element_sum[i_mapped - 1]) {
+ element_map[i_mapped] = i;
+ i_mapped++;
+ }
+ }
+ }
+ totmapped = i_mapped;
+
+ /* Finally assign elements to particles */
+ if ((part->flag & PART_TRAND) || (part->simplify_flag & PART_SIMPLIFY_ENABLE)) {
+ for (p = 0; p < totpart; p++) {
+ /* In theory element_sum[totmapped - 1] should be 1.0,
+ * but due to float errors this is not necessarily always true, so scale pos accordingly. */
+ const float pos = BLI_frand() * element_sum[totmapped - 1];
+ const int eidx = distribute_binary_search(element_sum, totmapped, pos);
+ particle_element[p] = element_map[eidx];
+ BLI_assert(pos <= element_sum[eidx]);
+ BLI_assert(eidx ? (pos > element_sum[eidx - 1]) : (pos >= 0.0f));
+ jitter_offset[particle_element[p]] = pos;
+ }
+ }
+ else {
+ double step, pos;
+
+ step = (totpart < 2) ? 0.5 : 1.0 / (double)totpart;
+ /* This is to address tricky issues with vertex-emitting when user tries (and expects) exact 1-1 vert/part
+ * distribution (see T47983 and its two example files). It allows us to consider pos as
+ * 'midpoint between v and v+1' (or 'p and p+1', depending whether we have more vertices than particles or not),
+ * and avoid stumbling over float imprecisions in element_sum. */
+ if (from == PART_FROM_VERT) {
+ pos = (totpart < totmapped) ? 0.5 / (double)totmapped : step * 0.5; /* We choose the smaller step. */
+ }
+ else {
+ pos = 0.0;
+ }
+
+ for (i = 0, p = 0; p < totpart; p++, pos += step) {
+ for ( ; (i < totmapped - 1) && (pos > (double)element_sum[i]); i++);
+
+ particle_element[p] = element_map[i];
+
+ jitter_offset[particle_element[p]] = pos;
+ }
+ }
+
+ MEM_freeN(element_sum);
+ MEM_freeN(element_map);
+
+ /* For hair, sort by origindex (allows optimization's in rendering), */
+ /* however with virtual parents the children need to be in random order. */
+ if (part->type == PART_HAIR && !(part->childtype==PART_CHILD_FACES && part->parents!=0.0f)) {
+ int *orig_index = NULL;
+
+ if (from == PART_FROM_VERT) {
+ if (dm->numVertData)
+ orig_index = dm->getVertDataArray(dm, CD_ORIGINDEX);
+ }
+ else {
+ if (dm->numTessFaceData)
+ orig_index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
+ }
+
+ if (orig_index) {
+ BLI_qsort_r(particle_element, totpart, sizeof(int), distribute_compare_orig_index, orig_index);
+ }
+ }
+
+ /* Create jittering if needed */
+ if (distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) {
+ jitlevel= part->userjit;
+
+ if (jitlevel == 0) {
+ jitlevel= totpart/totelem;
+ if (part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */
+ if (jitlevel<3) jitlevel= 3;
+ }
+
+ jit= MEM_callocN((2+ jitlevel*2)*sizeof(float), "jit");
+
+ /* for small amounts of particles we use regular jitter since it looks
+ * a bit better, for larger amounts we switch to hammersley sequence
+ * because it is much faster */
+ if (jitlevel < 25)
+ init_mv_jit(jit, jitlevel, psys->seed, part->jitfac);
+ else
+ hammersley_create(jit, jitlevel+1, psys->seed, part->jitfac);
+ BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */
+ }
+
+ /* Setup things for threaded distribution */
+ ctx->tree= tree;
+ ctx->seams= seams;
+ ctx->totseam= totseam;
+ ctx->sim.psys= psys;
+ ctx->index= particle_element;
+ ctx->jit= jit;
+ ctx->jitlevel= jitlevel;
+ ctx->jitoff= jitter_offset;
+ ctx->weight= element_weight;
+ ctx->maxweight= maxweight;
+ ctx->cfrom= cfrom;
+ ctx->distr= distr;
+ ctx->dm= dm;
+ ctx->tpars= tpars;
+
+ if (children) {
+ totpart= psys_render_simplify_distribution(ctx, totpart);
+ alloc_child_particles(psys, totpart);
+ }
+
+ return 1;
+}
+
+static void psys_task_init_distribute(ParticleTask *task, ParticleSimulationData *sim)
+{
+ /* init random number generator */
+ int seed = 31415926 + sim->psys->seed;
+
+ task->rng = BLI_rng_new(seed);
+}
+
+static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
+{
+ TaskScheduler *task_scheduler;
+ TaskPool *task_pool;
+ ParticleThreadContext ctx;
+ ParticleTask *tasks;
+ DerivedMesh *finaldm = sim->psmd->dm_final;
+ int i, totpart, numtasks;
+
+ /* create a task pool for distribution tasks */
+ if (!psys_thread_context_init_distribute(&ctx, sim, from))
+ return;
+
+ task_scheduler = BLI_task_scheduler_get();
+ task_pool = BLI_task_pool_create(task_scheduler, &ctx);
+
+ totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
+ psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);
+ for (i = 0; i < numtasks; ++i) {
+ ParticleTask *task = &tasks[i];
+
+ psys_task_init_distribute(task, sim);
+ if (from == PART_FROM_CHILD)
+ BLI_task_pool_push(task_pool, exec_distribute_child, task, false, TASK_PRIORITY_LOW);
+ else
+ BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, TASK_PRIORITY_LOW);
+ }
+ BLI_task_pool_work_and_wait(task_pool);
+
+ BLI_task_pool_free(task_pool);
+
+ psys_calc_dmcache(sim->ob, finaldm, sim->psmd->dm_deformed, sim->psys);
+
+ if (ctx.dm != finaldm)
+ ctx.dm->release(ctx.dm);
+
+ psys_tasks_free(tasks, numtasks);
+
+ psys_thread_context_free(&ctx);
+}
+
+/* ready for future use, to emit particles without geometry */
+static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSED(from))
+{
+ distribute_invalid(sim->scene, sim->psys, 0);
+
+ fprintf(stderr,"Shape emission not yet possible!\n");
+}
+
+void distribute_particles(ParticleSimulationData *sim, int from)
+{
+ PARTICLE_PSMD;
+ int distr_error=0;
+
+ if (psmd) {
+ if (psmd->dm_final)
+ distribute_particles_on_dm(sim, from);
+ else
+ distr_error=1;
+ }
+ else
+ distribute_particles_on_shape(sim, from);
+
+ if (distr_error) {
+ distribute_invalid(sim->scene, sim->psys, from);
+
+ fprintf(stderr,"Particle distribution error!\n");
+ }
+}
+
+/* ======== Simplify ======== */
+
+static float psys_render_viewport_falloff(double rate, float dist, float width)
+{
+ return pow(rate, dist / width);
+}
+
+static float psys_render_projected_area(ParticleSystem *psys, const float center[3], float area, double vprate, float *viewport)
+{
+ ParticleRenderData *data = psys->renderdata;
+ float co[4], view[3], ortho1[3], ortho2[3], w, dx, dy, radius;
+
+ /* transform to view space */
+ copy_v3_v3(co, center);
+ co[3] = 1.0f;
+ mul_m4_v4(data->viewmat, co);
+
+ /* compute two vectors orthogonal to view vector */
+ normalize_v3_v3(view, co);
+ ortho_basis_v3v3_v3(ortho1, ortho2, view);
+
+ /* compute on screen minification */
+ w = co[2] * data->winmat[2][3] + data->winmat[3][3];
+ dx = data->winx * ortho2[0] * data->winmat[0][0];
+ dy = data->winy * ortho2[1] * data->winmat[1][1];
+ w = sqrtf(dx * dx + dy * dy) / w;
+
+ /* w squared because we are working with area */
+ area = area * w * w;
+
+ /* viewport of the screen test */
+
+ /* project point on screen */
+ mul_m4_v4(data->winmat, co);
+ if (co[3] != 0.0f) {
+ co[0] = 0.5f * data->winx * (1.0f + co[0] / co[3]);
+ co[1] = 0.5f * data->winy * (1.0f + co[1] / co[3]);
+ }
+
+ /* screen space radius */
+ radius = sqrtf(area / (float)M_PI);
+
+ /* make smaller using fallof once over screen edge */
+ *viewport = 1.0f;
+
+ if (co[0] + radius < 0.0f)
+ *viewport *= psys_render_viewport_falloff(vprate, -(co[0] + radius), data->winx);
+ else if (co[0] - radius > data->winx)
+ *viewport *= psys_render_viewport_falloff(vprate, (co[0] - radius) - data->winx, data->winx);
+
+ if (co[1] + radius < 0.0f)
+ *viewport *= psys_render_viewport_falloff(vprate, -(co[1] + radius), data->winy);
+ else if (co[1] - radius > data->winy)
+ *viewport *= psys_render_viewport_falloff(vprate, (co[1] - radius) - data->winy, data->winy);
+
+ return area;
+}
+
+/* BMESH_TODO, for orig face data, we need to use MPoly */
+static int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot)
+{
+ DerivedMesh *dm = ctx->dm;
+ Mesh *me = (Mesh *)(ctx->sim.ob->data);
+ MFace *mf, *mface;
+ MVert *mvert;
+ ParticleRenderData *data;
+ ParticleRenderElem *elems, *elem;
+ ParticleSettings *part = ctx->sim.psys->part;
+ float *facearea, (*facecenter)[3], size[3], fac, powrate, scaleclamp;
+ float co1[3], co2[3], co3[3], co4[3], lambda, arearatio, t, area, viewport;
+ double vprate;
+ int *facetotvert;
+ int a, b, totorigface, totface, newtot, skipped;
+
+ /* double lookup */
+ const int *index_mf_to_mpoly;
+ const int *index_mp_to_orig;
+
+ if (part->ren_as != PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND))
+ return tot;
+ if (!ctx->sim.psys->renderdata)
+ return tot;
+
+ data = ctx->sim.psys->renderdata;
+ if (data->timeoffset)
+ return 0;
+ if (!(part->simplify_flag & PART_SIMPLIFY_ENABLE))
+ return tot;
+
+ mvert = dm->getVertArray(dm);
+ mface = dm->getTessFaceArray(dm);
+ totface = dm->getNumTessFaces(dm);
+ totorigface = me->totpoly;
+
+ if (totface == 0 || totorigface == 0)
+ return tot;
+
+ index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
+ index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX);
+ if (index_mf_to_mpoly == NULL) {
+ index_mp_to_orig = NULL;
+ }
+
+ facearea = MEM_callocN(sizeof(float) * totorigface, "SimplifyFaceArea");
+ facecenter = MEM_callocN(sizeof(float[3]) * totorigface, "SimplifyFaceCenter");
+ facetotvert = MEM_callocN(sizeof(int) * totorigface, "SimplifyFaceArea");
+ elems = MEM_callocN(sizeof(ParticleRenderElem) * totorigface, "SimplifyFaceElem");
+
+ if (data->elems)
+ MEM_freeN(data->elems);
+
+ data->do_simplify = true;
+ data->elems = elems;
+ data->index_mf_to_mpoly = index_mf_to_mpoly;
+ data->index_mp_to_orig = index_mp_to_orig;
+
+ /* compute number of children per original face */
+ for (a = 0; a < tot; a++) {
+ b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, ctx->index[a]) : ctx->index[a];
+ if (b != ORIGINDEX_NONE) {
+ elems[b].totchild++;
+ }
+ }
+
+ /* compute areas and centers of original faces */
+ for (mf = mface, a = 0; a < totface; a++, mf++) {
+ b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a;
+
+ if (b != ORIGINDEX_NONE) {
+ copy_v3_v3(co1, mvert[mf->v1].co);
+ copy_v3_v3(co2, mvert[mf->v2].co);
+ copy_v3_v3(co3, mvert[mf->v3].co);
+
+ add_v3_v3(facecenter[b], co1);
+ add_v3_v3(facecenter[b], co2);
+ add_v3_v3(facecenter[b], co3);
+
+ if (mf->v4) {
+ copy_v3_v3(co4, mvert[mf->v4].co);
+ add_v3_v3(facecenter[b], co4);
+ facearea[b] += area_quad_v3(co1, co2, co3, co4);
+ facetotvert[b] += 4;
+ }
+ else {
+ facearea[b] += area_tri_v3(co1, co2, co3);
+ facetotvert[b] += 3;
+ }
+ }
+ }
+
+ for (a = 0; a < totorigface; a++)
+ if (facetotvert[a] > 0)
+ mul_v3_fl(facecenter[a], 1.0f / facetotvert[a]);
+
+ /* for conversion from BU area / pixel area to reference screen size */
+ BKE_mesh_texspace_get(me, 0, 0, size);
+ fac = ((size[0] + size[1] + size[2]) / 3.0f) / part->simplify_refsize;
+ fac = fac * fac;
+
+ powrate = log(0.5f) / log(part->simplify_rate * 0.5f);
+ if (part->simplify_flag & PART_SIMPLIFY_VIEWPORT)
+ vprate = pow(1.0f - part->simplify_viewport, 5.0);
+ else
+ vprate = 1.0;
+
+ /* set simplification parameters per original face */
+ for (a = 0, elem = elems; a < totorigface; a++, elem++) {
+ area = psys_render_projected_area(ctx->sim.psys, facecenter[a], facearea[a], vprate, &viewport);
+ arearatio = fac * area / facearea[a];
+
+ if ((arearatio < 1.0f || viewport < 1.0f) && elem->totchild) {
+ /* lambda is percentage of elements to keep */
+ lambda = (arearatio < 1.0f) ? powf(arearatio, powrate) : 1.0f;
+ lambda *= viewport;
+
+ lambda = MAX2(lambda, 1.0f / elem->totchild);
+
+ /* compute transition region */
+ t = part->simplify_transition;
+ elem->t = (lambda - t < 0.0f) ? lambda : (lambda + t > 1.0f) ? 1.0f - lambda : t;
+ elem->reduce = 1;
+
+ /* scale at end and beginning of the transition region */
+ elem->scalemax = (lambda + t < 1.0f) ? 1.0f / lambda : 1.0f / (1.0f - elem->t * elem->t / t);
+ elem->scalemin = (lambda + t < 1.0f) ? 0.0f : elem->scalemax * (1.0f - elem->t / t);
+
+ elem->scalemin = sqrtf(elem->scalemin);
+ elem->scalemax = sqrtf(elem->scalemax);
+
+ /* clamp scaling */
+ scaleclamp = (float)min_ii(elem->totchild, 10);
+ elem->scalemin = MIN2(scaleclamp, elem->scalemin);
+ elem->scalemax = MIN2(scaleclamp, elem->scalemax);
+
+ /* extend lambda to include transition */
+ lambda = lambda + elem->t;
+ if (lambda > 1.0f)
+ lambda = 1.0f;
+ }
+ else {
+ lambda = arearatio;
+
+ elem->scalemax = 1.0f; //sqrt(lambda);
+ elem->scalemin = 1.0f; //sqrt(lambda);
+ elem->reduce = 0;
+ }
+
+ elem->lambda = lambda;
+ elem->scalemin = sqrtf(elem->scalemin);
+ elem->scalemax = sqrtf(elem->scalemax);
+ elem->curchild = 0;
+ }
+
+ MEM_freeN(facearea);
+ MEM_freeN(facecenter);
+ MEM_freeN(facetotvert);
+
+ /* move indices and set random number skipping */
+ ctx->skip = MEM_callocN(sizeof(int) * tot, "SimplificationSkip");
+
+ skipped = 0;
+ for (a = 0, newtot = 0; a < tot; a++) {
+ b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, ctx->index[a]) : ctx->index[a];
+
+ if (b != ORIGINDEX_NONE) {
+ if (elems[b].curchild++ < ceil(elems[b].lambda * elems[b].totchild)) {
+ ctx->index[newtot] = ctx->index[a];
+ ctx->skip[newtot] = skipped;
+ skipped = 0;
+ newtot++;
+ }
+ else skipped++;
+ }
+ else skipped++;
+ }
+
+ for (a = 0, elem = elems; a < totorigface; a++, elem++)
+ elem->curchild = 0;
+
+ return newtot;
+}
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
new file mode 100644
index 00000000000..ee435051151
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -0,0 +1,4362 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Raul Fernandez Hernandez (Farsthary), Stephen Swhitehorn.
+ *
+ * Adaptive time step
+ * Classical SPH
+ * Copyright 2011-2012 AutoCRC
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/particle_system.c
+ * \ingroup bke
+ */
+
+
+#include <stddef.h>
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_boid_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_listBase.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_edgehash.h"
+#include "BLI_rand.h"
+#include "BLI_jitter.h"
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_kdtree.h"
+#include "BLI_kdopbvh.h"
+#include "BLI_sort.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
+#include "BLI_linklist.h"
+
+#include "BKE_animsys.h"
+#include "BKE_boids.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_collision.h"
+#include "BKE_colortools.h"
+#include "BKE_effect.h"
+#include "BKE_library_query.h"
+#include "BKE_particle.h"
+#include "BKE_global.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_object.h"
+#include "BKE_material.h"
+#include "BKE_cloth.h"
+#include "BKE_lattice.h"
+#include "BKE_pointcache.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_scene.h"
+#include "BKE_bvhutils.h"
+#include "BKE_depsgraph.h"
+
+#include "PIL_time.h"
+
+#include "RE_shader_ext.h"
+
+/* fluid sim particle import */
+#ifdef WITH_MOD_FLUID
+#include "DNA_object_fluidsim.h"
+#include "LBM_fluidsim.h"
+#include <zlib.h>
+#include <string.h>
+
+#endif // WITH_MOD_FLUID
+
+static ThreadRWMutex psys_bvhtree_rwlock = BLI_RWLOCK_INITIALIZER;
+
+/************************************************/
+/* Reacting to system events */
+/************************************************/
+
+static int particles_are_dynamic(ParticleSystem *psys)
+{
+ if (psys->pointcache->flag & PTCACHE_BAKED)
+ return 0;
+
+ if (psys->part->type == PART_HAIR)
+ return psys->flag & PSYS_HAIR_DYNAMICS;
+ else
+ return ELEM(psys->part->phystype, PART_PHYS_NEWTON, PART_PHYS_BOIDS, PART_PHYS_FLUID);
+}
+
+float psys_get_current_display_percentage(ParticleSystem *psys)
+{
+ ParticleSettings *part=psys->part;
+
+ if ((psys->renderdata && !particles_are_dynamic(psys)) || /* non-dynamic particles can be rendered fully */
+ (part->child_nbr && part->childtype) || /* display percentage applies to children */
+ (psys->pointcache->flag & PTCACHE_BAKING)) /* baking is always done with full amount */
+ {
+ return 1.0f;
+ }
+
+ return psys->part->disp/100.0f;
+}
+
+static int tot_particles(ParticleSystem *psys, PTCacheID *pid)
+{
+ if (pid && psys->pointcache->flag & PTCACHE_EXTERNAL)
+ return pid->cache->totpoint;
+ else if (psys->part->distr == PART_DISTR_GRID && psys->part->from != PART_FROM_VERT)
+ return psys->part->grid_res * psys->part->grid_res * psys->part->grid_res - psys->totunexist;
+ else
+ return psys->part->totpart - psys->totunexist;
+}
+
+void psys_reset(ParticleSystem *psys, int mode)
+{
+ PARTICLE_P;
+
+ if (ELEM(mode, PSYS_RESET_ALL, PSYS_RESET_DEPSGRAPH)) {
+ if (mode == PSYS_RESET_ALL || !(psys->flag & PSYS_EDITED)) {
+ /* don't free if not absolutely necessary */
+ if (psys->totpart != tot_particles(psys, NULL)) {
+ psys_free_particles(psys);
+ psys->totpart= 0;
+ }
+
+ psys->totkeyed= 0;
+ psys->flag &= ~(PSYS_HAIR_DONE|PSYS_KEYED);
+
+ if (psys->edit && psys->free_edit) {
+ psys->free_edit(psys->edit);
+ psys->edit = NULL;
+ psys->free_edit = NULL;
+ }
+ }
+ }
+ else if (mode == PSYS_RESET_CACHE_MISS) {
+ /* set all particles to be skipped */
+ LOOP_PARTICLES
+ pa->flag |= PARS_NO_DISP;
+ }
+
+ /* reset children */
+ if (psys->child) {
+ MEM_freeN(psys->child);
+ psys->child= NULL;
+ }
+
+ psys->totchild= 0;
+
+ /* reset path cache */
+ psys_free_path_cache(psys, psys->edit);
+
+ /* reset point cache */
+ BKE_ptcache_invalidate(psys->pointcache);
+
+ if (psys->fluid_springs) {
+ MEM_freeN(psys->fluid_springs);
+ psys->fluid_springs = NULL;
+ }
+
+ psys->tot_fluidsprings = psys->alloc_fluidsprings = 0;
+}
+
+static void realloc_particles(ParticleSimulationData *sim, int new_totpart)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleData *newpars = NULL;
+ BoidParticle *newboids = NULL;
+ PARTICLE_P;
+ int totpart, totsaved = 0;
+
+ if (new_totpart<0) {
+ if ((part->distr == PART_DISTR_GRID) && (part->from != PART_FROM_VERT)) {
+ totpart= part->grid_res;
+ totpart*=totpart*totpart;
+ }
+ else
+ totpart=part->totpart;
+ }
+ else
+ totpart=new_totpart;
+
+ if (totpart != psys->totpart) {
+ if (psys->edit && psys->free_edit) {
+ psys->free_edit(psys->edit);
+ psys->edit = NULL;
+ psys->free_edit = NULL;
+ }
+
+ if (totpart) {
+ newpars= MEM_callocN(totpart*sizeof(ParticleData), "particles");
+ if (newpars == NULL)
+ return;
+
+ if (psys->part->phystype == PART_PHYS_BOIDS) {
+ newboids= MEM_callocN(totpart*sizeof(BoidParticle), "boid particles");
+
+ if (newboids == NULL) {
+ /* allocation error! */
+ if (newpars)
+ MEM_freeN(newpars);
+ return;
+ }
+ }
+ }
+
+ if (psys->particles) {
+ totsaved=MIN2(psys->totpart,totpart);
+ /*save old pars*/
+ if (totsaved) {
+ memcpy(newpars,psys->particles,totsaved*sizeof(ParticleData));
+
+ if (psys->particles->boid)
+ memcpy(newboids, psys->particles->boid, totsaved*sizeof(BoidParticle));
+ }
+
+ if (psys->particles->keys)
+ MEM_freeN(psys->particles->keys);
+
+ if (psys->particles->boid)
+ MEM_freeN(psys->particles->boid);
+
+ for (p=0, pa=newpars; p<totsaved; p++, pa++) {
+ if (pa->keys) {
+ pa->keys= NULL;
+ pa->totkey= 0;
+ }
+ }
+
+ for (p=totsaved, pa=psys->particles+totsaved; p<psys->totpart; p++, pa++)
+ if (pa->hair) MEM_freeN(pa->hair);
+
+ MEM_freeN(psys->particles);
+ psys_free_pdd(psys);
+ }
+
+ psys->particles=newpars;
+ psys->totpart=totpart;
+
+ if (newboids) {
+ LOOP_PARTICLES
+ pa->boid = newboids++;
+ }
+ }
+
+ if (psys->child) {
+ MEM_freeN(psys->child);
+ psys->child=NULL;
+ psys->totchild=0;
+ }
+}
+
+int psys_get_child_number(Scene *scene, ParticleSystem *psys)
+{
+ int nbr;
+
+ if (!psys->part->childtype)
+ return 0;
+
+ if (psys->renderdata)
+ nbr= psys->part->ren_child_nbr;
+ else
+ nbr= psys->part->child_nbr;
+
+ return get_render_child_particle_number(&scene->r, nbr, psys->renderdata != NULL);
+}
+
+int psys_get_tot_child(Scene *scene, ParticleSystem *psys)
+{
+ return psys->totpart*psys_get_child_number(scene, psys);
+}
+
+/************************************************/
+/* Distribution */
+/************************************************/
+
+void psys_calc_dmcache(Object *ob, DerivedMesh *dm_final, DerivedMesh *dm_deformed, ParticleSystem *psys)
+{
+ /* use for building derived mesh mapping info:
+ *
+ * node: the allocated links - total derived mesh element count
+ * nodearray: the array of nodes aligned with the base mesh's elements, so
+ * each original elements can reference its derived elements
+ */
+ Mesh *me= (Mesh*)ob->data;
+ bool use_modifier_stack= psys->part->use_modifier_stack;
+ PARTICLE_P;
+
+ /* CACHE LOCATIONS */
+ if (!dm_final->deformedOnly) {
+ /* Will use later to speed up subsurf/derivedmesh */
+ LinkNode *node, *nodedmelem, **nodearray;
+ int totdmelem, totelem, i, *origindex, *origindex_poly = NULL;
+
+ if (psys->part->from == PART_FROM_VERT) {
+ totdmelem= dm_final->getNumVerts(dm_final);
+
+ if (use_modifier_stack) {
+ totelem= totdmelem;
+ origindex= NULL;
+ }
+ else {
+ totelem= me->totvert;
+ origindex= dm_final->getVertDataArray(dm_final, CD_ORIGINDEX);
+ }
+ }
+ else { /* FROM_FACE/FROM_VOLUME */
+ totdmelem= dm_final->getNumTessFaces(dm_final);
+
+ if (use_modifier_stack) {
+ totelem= totdmelem;
+ origindex= NULL;
+ origindex_poly= NULL;
+ }
+ else {
+ totelem = dm_deformed->getNumTessFaces(dm_deformed);
+ origindex = dm_final->getTessFaceDataArray(dm_final, CD_ORIGINDEX);
+
+ /* for face lookups we need the poly origindex too */
+ origindex_poly= dm_final->getPolyDataArray(dm_final, CD_ORIGINDEX);
+ if (origindex_poly == NULL) {
+ origindex= NULL;
+ }
+ }
+ }
+
+ nodedmelem= MEM_callocN(sizeof(LinkNode)*totdmelem, "psys node elems");
+ nodearray= MEM_callocN(sizeof(LinkNode *)*totelem, "psys node array");
+
+ for (i=0, node=nodedmelem; i<totdmelem; i++, node++) {
+ int origindex_final;
+ node->link = SET_INT_IN_POINTER(i);
+
+ /* may be vertex or face origindex */
+ if (use_modifier_stack) {
+ origindex_final = i;
+ }
+ else {
+ origindex_final = origindex ? origindex[i] : ORIGINDEX_NONE;
+
+ /* if we have a poly source, do an index lookup */
+ if (origindex_poly && origindex_final != ORIGINDEX_NONE) {
+ origindex_final = origindex_poly[origindex_final];
+ }
+ }
+
+ if (origindex_final != ORIGINDEX_NONE && origindex_final < totelem) {
+ if (nodearray[origindex_final]) {
+ /* prepend */
+ node->next = nodearray[origindex_final];
+ nodearray[origindex_final] = node;
+ }
+ else {
+ nodearray[origindex_final] = node;
+ }
+ }
+ }
+
+ /* cache the verts/faces! */
+ LOOP_PARTICLES {
+ if (pa->num < 0) {
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+ continue;
+ }
+
+ if (use_modifier_stack) {
+ if (pa->num < totelem)
+ pa->num_dmcache = DMCACHE_ISCHILD;
+ else
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+ }
+ else {
+ if (psys->part->from == PART_FROM_VERT) {
+ if (pa->num < totelem && nodearray[pa->num])
+ pa->num_dmcache= GET_INT_FROM_POINTER(nodearray[pa->num]->link);
+ else
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+ }
+ else { /* FROM_FACE/FROM_VOLUME */
+ pa->num_dmcache = psys_particle_dm_face_lookup(dm_final, dm_deformed, pa->num, pa->fuv, nodearray);
+ }
+ }
+ }
+
+ MEM_freeN(nodearray);
+ MEM_freeN(nodedmelem);
+ }
+ else {
+ /* TODO PARTICLE, make the following line unnecessary, each function
+ * should know to use the num or num_dmcache, set the num_dmcache to
+ * an invalid value, just in case */
+
+ LOOP_PARTICLES {
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+ }
+ }
+}
+
+/* threaded child particle distribution and path caching */
+void psys_thread_context_init(ParticleThreadContext *ctx, ParticleSimulationData *sim)
+{
+ memset(ctx, 0, sizeof(ParticleThreadContext));
+ ctx->sim = *sim;
+ ctx->dm = ctx->sim.psmd->dm_final;
+ ctx->ma = give_current_material(sim->ob, sim->psys->part->omat);
+}
+
+#define MAX_PARTICLES_PER_TASK 256 /* XXX arbitrary - maybe use at least number of points instead for better balancing? */
+
+BLI_INLINE int ceil_ii(int a, int b)
+{
+ return (a + b - 1) / b;
+}
+
+void psys_tasks_create(ParticleThreadContext *ctx, int startpart, int endpart, ParticleTask **r_tasks, int *r_numtasks)
+{
+ ParticleTask *tasks;
+ int numtasks = ceil_ii((endpart - startpart), MAX_PARTICLES_PER_TASK);
+ float particles_per_task = (float)(endpart - startpart) / (float)numtasks, p, pnext;
+ int i;
+
+ tasks = MEM_callocN(sizeof(ParticleTask) * numtasks, "ParticleThread");
+ *r_numtasks = numtasks;
+ *r_tasks = tasks;
+
+ p = (float)startpart;
+ for (i = 0; i < numtasks; i++, p = pnext) {
+ pnext = p + particles_per_task;
+
+ tasks[i].ctx = ctx;
+ tasks[i].begin = (int)p;
+ tasks[i].end = min_ii((int)pnext, endpart);
+ }
+}
+
+void psys_tasks_free(ParticleTask *tasks, int numtasks)
+{
+ int i;
+
+ /* threads */
+ for (i = 0; i < numtasks; ++i) {
+ if (tasks[i].rng)
+ BLI_rng_free(tasks[i].rng);
+ if (tasks[i].rng_path)
+ BLI_rng_free(tasks[i].rng_path);
+ }
+
+ MEM_freeN(tasks);
+}
+
+void psys_thread_context_free(ParticleThreadContext *ctx)
+{
+ /* path caching */
+ if (ctx->vg_length)
+ MEM_freeN(ctx->vg_length);
+ if (ctx->vg_clump)
+ MEM_freeN(ctx->vg_clump);
+ if (ctx->vg_kink)
+ MEM_freeN(ctx->vg_kink);
+ if (ctx->vg_rough1)
+ MEM_freeN(ctx->vg_rough1);
+ if (ctx->vg_rough2)
+ MEM_freeN(ctx->vg_rough2);
+ if (ctx->vg_roughe)
+ MEM_freeN(ctx->vg_roughe);
+
+ if (ctx->sim.psys->lattice_deform_data) {
+ end_latt_deform(ctx->sim.psys->lattice_deform_data);
+ ctx->sim.psys->lattice_deform_data = NULL;
+ }
+
+ /* distribution */
+ if (ctx->jit) MEM_freeN(ctx->jit);
+ if (ctx->jitoff) MEM_freeN(ctx->jitoff);
+ if (ctx->weight) MEM_freeN(ctx->weight);
+ if (ctx->index) MEM_freeN(ctx->index);
+ if (ctx->skip) MEM_freeN(ctx->skip);
+ if (ctx->seams) MEM_freeN(ctx->seams);
+ //if (ctx->vertpart) MEM_freeN(ctx->vertpart);
+ BLI_kdtree_free(ctx->tree);
+
+ if (ctx->clumpcurve != NULL) {
+ curvemapping_free(ctx->clumpcurve);
+ }
+ if (ctx->roughcurve != NULL) {
+ curvemapping_free(ctx->roughcurve);
+ }
+}
+
+static void initialize_particle_texture(ParticleSimulationData *sim, ParticleData *pa, int p)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleTexture ptex;
+
+ psys_get_texture(sim, pa, &ptex, PAMAP_INIT, 0.f);
+
+ switch (part->type) {
+ case PART_EMITTER:
+ if (ptex.exist < psys_frand(psys, p+125))
+ pa->flag |= PARS_UNEXIST;
+ pa->time = part->sta + (part->end - part->sta)*ptex.time;
+ break;
+ case PART_HAIR:
+ if (ptex.exist < psys_frand(psys, p+125))
+ pa->flag |= PARS_UNEXIST;
+ pa->time = 0.f;
+ break;
+ case PART_FLUID:
+ break;
+ }
+}
+
+/* set particle parameters that don't change during particle's life */
+void initialize_particle(ParticleSimulationData *sim, ParticleData *pa)
+{
+ ParticleSettings *part = sim->psys->part;
+ float birth_time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart;
+
+ pa->flag &= ~PARS_UNEXIST;
+ pa->time = part->sta + (part->end - part->sta) * birth_time;
+
+ pa->hair_index = 0;
+ /* we can't reset to -1 anymore since we've figured out correct index in distribute_particles */
+ /* usage other than straight after distribute has to handle this index by itself - jahka*/
+ //pa->num_dmcache = DMCACHE_NOTFOUND; /* assume we don't have a derived mesh face */
+}
+
+static void initialize_all_particles(ParticleSimulationData *sim)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ /* Grid distributionsets UNEXIST flag, need to take care of
+ * it here because later this flag is being reset.
+ *
+ * We can't do it for any distribution, because it'll then
+ * conflict with texture influence, which does not free
+ * unexisting particles and only sets flag.
+ *
+ * It's not so bad, because only grid distribution sets
+ * UNEXIST flag.
+ */
+ const bool emit_from_volume_grid = (part->distr == PART_DISTR_GRID) &&
+ (!ELEM(part->from, PART_FROM_VERT, PART_FROM_CHILD));
+ PARTICLE_P;
+ LOOP_PARTICLES {
+ if (!(emit_from_volume_grid && (pa->flag & PARS_UNEXIST) != 0)) {
+ initialize_particle(sim, pa);
+ }
+ }
+}
+
+static void free_unexisting_particles(ParticleSimulationData *sim)
+{
+ ParticleSystem *psys = sim->psys;
+ PARTICLE_P;
+
+ psys->totunexist = 0;
+
+ LOOP_PARTICLES {
+ if (pa->flag & PARS_UNEXIST) {
+ psys->totunexist++;
+ }
+ }
+
+ if (psys->totpart && psys->totunexist == psys->totpart) {
+ if (psys->particles->boid)
+ MEM_freeN(psys->particles->boid);
+
+ MEM_freeN(psys->particles);
+ psys->particles = NULL;
+ psys->totpart = psys->totunexist = 0;
+ }
+
+ if (psys->totunexist) {
+ int newtotpart = psys->totpart - psys->totunexist;
+ ParticleData *npa, *newpars;
+
+ npa = newpars = MEM_callocN(newtotpart * sizeof(ParticleData), "particles");
+
+ for (p=0, pa=psys->particles; p<newtotpart; p++, pa++, npa++) {
+ while (pa->flag & PARS_UNEXIST)
+ pa++;
+
+ memcpy(npa, pa, sizeof(ParticleData));
+ }
+
+ if (psys->particles->boid)
+ MEM_freeN(psys->particles->boid);
+ MEM_freeN(psys->particles);
+ psys->particles = newpars;
+ psys->totpart -= psys->totunexist;
+
+ if (psys->particles->boid) {
+ BoidParticle *newboids = MEM_callocN(psys->totpart * sizeof(BoidParticle), "boid particles");
+
+ LOOP_PARTICLES {
+ pa->boid = newboids++;
+ }
+
+ }
+ }
+}
+
+static void get_angular_velocity_vector(short avemode, ParticleKey *state, float vec[3])
+{
+ switch (avemode) {
+ case PART_AVE_VELOCITY:
+ copy_v3_v3(vec, state->vel);
+ break;
+ case PART_AVE_HORIZONTAL:
+ {
+ float zvec[3];
+ zvec[0] = zvec[1] = 0;
+ zvec[2] = 1.f;
+ cross_v3_v3v3(vec, state->vel, zvec);
+ break;
+ }
+ case PART_AVE_VERTICAL:
+ {
+ float zvec[3], temp[3];
+ zvec[0] = zvec[1] = 0;
+ zvec[2] = 1.f;
+ cross_v3_v3v3(temp, state->vel, zvec);
+ cross_v3_v3v3(vec, temp, state->vel);
+ break;
+ }
+ case PART_AVE_GLOBAL_X:
+ vec[0] = 1.f;
+ vec[1] = vec[2] = 0;
+ break;
+ case PART_AVE_GLOBAL_Y:
+ vec[1] = 1.f;
+ vec[0] = vec[2] = 0;
+ break;
+ case PART_AVE_GLOBAL_Z:
+ vec[2] = 1.f;
+ vec[0] = vec[1] = 0;
+ break;
+ }
+}
+
+void psys_get_birth_coords(ParticleSimulationData *sim, ParticleData *pa, ParticleKey *state, float dtime, float cfra)
+{
+ Object *ob = sim->ob;
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleTexture ptex;
+ float fac, phasefac, nor[3] = {0,0,0},loc[3],vel[3] = {0.0,0.0,0.0},rot[4],q2[4];
+ float r_vel[3],r_ave[3],r_rot[4],vec[3],p_vel[3] = {0.0,0.0,0.0};
+ float x_vec[3] = {1.0,0.0,0.0}, utan[3] = {0.0,1.0,0.0}, vtan[3] = {0.0,0.0,1.0}, rot_vec[3] = {0.0,0.0,0.0};
+ float q_phase[4];
+
+ const bool use_boids = ((part->phystype == PART_PHYS_BOIDS) &&
+ (pa->boid != NULL));
+ const bool use_tangents = ((use_boids == false) &&
+ ((part->tanfac != 0.0f) || (part->rotmode == PART_ROT_NOR_TAN)));
+
+ int p = pa - psys->particles;
+
+ /* get birth location from object */
+ if (use_tangents)
+ psys_particle_on_emitter(sim->psmd, part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,utan,vtan,0,0);
+ else
+ psys_particle_on_emitter(sim->psmd, part->from,pa->num, pa->num_dmcache, pa->fuv,pa->foffset,loc,nor,0,0,0,0);
+
+ /* get possible textural influence */
+ psys_get_texture(sim, pa, &ptex, PAMAP_IVEL, cfra);
+
+ /* particles live in global space so */
+ /* let's convert: */
+ /* -location */
+ mul_m4_v3(ob->obmat, loc);
+
+ /* -normal */
+ mul_mat3_m4_v3(ob->obmat, nor);
+ normalize_v3(nor);
+
+ /* -tangent */
+ if (use_tangents) {
+ //float phase=vg_rot?2.0f*(psys_particle_value_from_verts(sim->psmd->dm,part->from,pa,vg_rot)-0.5f):0.0f;
+ float phase=0.0f;
+ mul_v3_fl(vtan,-cosf((float)M_PI*(part->tanphase+phase)));
+ fac= -sinf((float)M_PI*(part->tanphase+phase));
+ madd_v3_v3fl(vtan, utan, fac);
+
+ mul_mat3_m4_v3(ob->obmat,vtan);
+
+ copy_v3_v3(utan, nor);
+ mul_v3_fl(utan,dot_v3v3(vtan,nor));
+ sub_v3_v3(vtan, utan);
+
+ normalize_v3(vtan);
+ }
+
+
+ /* -velocity (boids need this even if there's no random velocity) */
+ if (part->randfac != 0.0f || (part->phystype==PART_PHYS_BOIDS && pa->boid)) {
+ r_vel[0] = 2.0f * (psys_frand(psys, p + 10) - 0.5f);
+ r_vel[1] = 2.0f * (psys_frand(psys, p + 11) - 0.5f);
+ r_vel[2] = 2.0f * (psys_frand(psys, p + 12) - 0.5f);
+
+ mul_mat3_m4_v3(ob->obmat, r_vel);
+ normalize_v3(r_vel);
+ }
+
+ /* -angular velocity */
+ if (part->avemode==PART_AVE_RAND) {
+ r_ave[0] = 2.0f * (psys_frand(psys, p + 13) - 0.5f);
+ r_ave[1] = 2.0f * (psys_frand(psys, p + 14) - 0.5f);
+ r_ave[2] = 2.0f * (psys_frand(psys, p + 15) - 0.5f);
+
+ mul_mat3_m4_v3(ob->obmat,r_ave);
+ normalize_v3(r_ave);
+ }
+
+ /* -rotation */
+ if (part->randrotfac != 0.0f) {
+ r_rot[0] = 2.0f * (psys_frand(psys, p + 16) - 0.5f);
+ r_rot[1] = 2.0f * (psys_frand(psys, p + 17) - 0.5f);
+ r_rot[2] = 2.0f * (psys_frand(psys, p + 18) - 0.5f);
+ r_rot[3] = 2.0f * (psys_frand(psys, p + 19) - 0.5f);
+ normalize_qt(r_rot);
+
+ mat4_to_quat(rot,ob->obmat);
+ mul_qt_qtqt(r_rot,r_rot,rot);
+ }
+
+ if (use_boids) {
+ float dvec[3], q[4], mat[3][3];
+
+ copy_v3_v3(state->co,loc);
+
+ /* boids don't get any initial velocity */
+ zero_v3(state->vel);
+
+ /* boids store direction in ave */
+ if (fabsf(nor[2])==1.0f) {
+ sub_v3_v3v3(state->ave, loc, ob->obmat[3]);
+ normalize_v3(state->ave);
+ }
+ else {
+ copy_v3_v3(state->ave, nor);
+ }
+
+ /* calculate rotation matrix */
+ project_v3_v3v3(dvec, r_vel, state->ave);
+ sub_v3_v3v3(mat[0], state->ave, dvec);
+ normalize_v3(mat[0]);
+ negate_v3_v3(mat[2], r_vel);
+ normalize_v3(mat[2]);
+ cross_v3_v3v3(mat[1], mat[2], mat[0]);
+
+ /* apply rotation */
+ mat3_to_quat_is_ok( q,mat);
+ copy_qt_qt(state->rot, q);
+ }
+ else {
+ /* conversion done so now we apply new: */
+ /* -velocity from: */
+
+ /* *reactions */
+ if (dtime > 0.f) {
+ sub_v3_v3v3(vel, pa->state.vel, pa->prev_state.vel);
+ }
+
+ /* *emitter velocity */
+ if (dtime != 0.f && part->obfac != 0.f) {
+ sub_v3_v3v3(vel, loc, state->co);
+ mul_v3_fl(vel, part->obfac/dtime);
+ }
+
+ /* *emitter normal */
+ if (part->normfac != 0.f)
+ madd_v3_v3fl(vel, nor, part->normfac);
+
+ /* *emitter tangent */
+ if (sim->psmd && part->tanfac != 0.f)
+ madd_v3_v3fl(vel, vtan, part->tanfac);
+
+ /* *emitter object orientation */
+ if (part->ob_vel[0] != 0.f) {
+ normalize_v3_v3(vec, ob->obmat[0]);
+ madd_v3_v3fl(vel, vec, part->ob_vel[0]);
+ }
+ if (part->ob_vel[1] != 0.f) {
+ normalize_v3_v3(vec, ob->obmat[1]);
+ madd_v3_v3fl(vel, vec, part->ob_vel[1]);
+ }
+ if (part->ob_vel[2] != 0.f) {
+ normalize_v3_v3(vec, ob->obmat[2]);
+ madd_v3_v3fl(vel, vec, part->ob_vel[2]);
+ }
+
+ /* *texture */
+ /* TODO */
+
+ /* *random */
+ if (part->randfac != 0.f)
+ madd_v3_v3fl(vel, r_vel, part->randfac);
+
+ /* *particle */
+ if (part->partfac != 0.f)
+ madd_v3_v3fl(vel, p_vel, part->partfac);
+
+ mul_v3_v3fl(state->vel, vel, ptex.ivel);
+
+ /* -location from emitter */
+ copy_v3_v3(state->co,loc);
+
+ /* -rotation */
+ unit_qt(state->rot);
+
+ if (part->rotmode) {
+ bool use_global_space;
+
+ /* create vector into which rotation is aligned */
+ switch (part->rotmode) {
+ case PART_ROT_NOR:
+ case PART_ROT_NOR_TAN:
+ copy_v3_v3(rot_vec, nor);
+ use_global_space = false;
+ break;
+ case PART_ROT_VEL:
+ copy_v3_v3(rot_vec, vel);
+ use_global_space = true;
+ break;
+ case PART_ROT_GLOB_X:
+ case PART_ROT_GLOB_Y:
+ case PART_ROT_GLOB_Z:
+ rot_vec[part->rotmode - PART_ROT_GLOB_X] = 1.0f;
+ use_global_space = true;
+ break;
+ case PART_ROT_OB_X:
+ case PART_ROT_OB_Y:
+ case PART_ROT_OB_Z:
+ copy_v3_v3(rot_vec, ob->obmat[part->rotmode - PART_ROT_OB_X]);
+ use_global_space = false;
+ break;
+ default:
+ use_global_space = true;
+ break;
+ }
+
+ /* create rotation quat */
+
+
+ if (use_global_space) {
+ negate_v3(rot_vec);
+ vec_to_quat(q2, rot_vec, OB_POSX, OB_POSZ);
+
+ /* randomize rotation quat */
+ if (part->randrotfac != 0.0f) {
+ interp_qt_qtqt(rot, q2, r_rot, part->randrotfac);
+ }
+ else {
+ copy_qt_qt(rot, q2);
+ }
+ }
+ else {
+ /* calculate rotation in local-space */
+ float q_obmat[4];
+ float q_imat[4];
+
+ mat4_to_quat(q_obmat, ob->obmat);
+ invert_qt_qt_normalized(q_imat, q_obmat);
+
+
+ if (part->rotmode != PART_ROT_NOR_TAN) {
+ float rot_vec_local[3];
+
+ /* rot_vec */
+ negate_v3(rot_vec);
+ copy_v3_v3(rot_vec_local, rot_vec);
+ mul_qt_v3(q_imat, rot_vec_local);
+ normalize_v3(rot_vec_local);
+
+ vec_to_quat(q2, rot_vec_local, OB_POSX, OB_POSZ);
+ }
+ else {
+ /* (part->rotmode == PART_ROT_NOR_TAN) */
+ float tmat[3][3];
+
+ /* note: utan_local is not taken from 'utan', we calculate from rot_vec/vtan */
+ /* note: it looks like rotation phase may be applied twice (once with vtan, again below)
+ * however this isn't the case - campbell */
+ float *rot_vec_local = tmat[0];
+ float *vtan_local = tmat[1];
+ float *utan_local = tmat[2];
+
+ /* use tangents */
+ BLI_assert(use_tangents == true);
+
+ /* rot_vec */
+ copy_v3_v3(rot_vec_local, rot_vec);
+ mul_qt_v3(q_imat, rot_vec_local);
+
+ /* vtan_local */
+ copy_v3_v3(vtan_local, vtan); /* flips, cant use */
+ mul_qt_v3(q_imat, vtan_local);
+
+ /* ensure orthogonal matrix (rot_vec aligned) */
+ cross_v3_v3v3(utan_local, vtan_local, rot_vec_local);
+ cross_v3_v3v3(vtan_local, utan_local, rot_vec_local);
+
+ /* note: no need to normalize */
+ mat3_to_quat(q2, tmat);
+ }
+
+ /* randomize rotation quat */
+ if (part->randrotfac != 0.0f) {
+ mul_qt_qtqt(r_rot, r_rot, q_imat);
+ interp_qt_qtqt(rot, q2, r_rot, part->randrotfac);
+ }
+ else {
+ copy_qt_qt(rot, q2);
+ }
+
+ mul_qt_qtqt(rot, q_obmat, rot);
+ }
+
+ /* rotation phase */
+ phasefac = part->phasefac;
+ if (part->randphasefac != 0.0f)
+ phasefac += part->randphasefac * psys_frand(psys, p + 20);
+ axis_angle_to_quat( q_phase,x_vec, phasefac*(float)M_PI);
+
+ /* combine base rotation & phase */
+ mul_qt_qtqt(state->rot, rot, q_phase);
+ }
+
+ /* -angular velocity */
+
+ zero_v3(state->ave);
+
+ if (part->avemode) {
+ if (part->avemode == PART_AVE_RAND)
+ copy_v3_v3(state->ave, r_ave);
+ else
+ get_angular_velocity_vector(part->avemode, state, state->ave);
+
+ normalize_v3(state->ave);
+ mul_v3_fl(state->ave, part->avefac);
+ }
+ }
+}
+
+/* recursively evaluate emitter parent anim at cfra */
+static void evaluate_emitter_anim(Scene *scene, Object *ob, float cfra)
+{
+ if (ob->parent)
+ evaluate_emitter_anim(scene, ob->parent, cfra);
+
+ /* we have to force RECALC_ANIM here since where_is_objec_time only does drivers */
+ BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, cfra, ADT_RECALC_ANIM);
+ BKE_object_where_is_calc_time(scene, ob, cfra);
+}
+
+/* sets particle to the emitter surface with initial velocity & rotation */
+void reset_particle(ParticleSimulationData *sim, ParticleData *pa, float dtime, float cfra)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part;
+ ParticleTexture ptex;
+ int p = pa - psys->particles;
+ part=psys->part;
+
+ /* get precise emitter matrix if particle is born */
+ if (part->type!=PART_HAIR && dtime > 0.f && pa->time < cfra && pa->time >= sim->psys->cfra) {
+ evaluate_emitter_anim(sim->scene, sim->ob, pa->time);
+
+ psys->flag |= PSYS_OB_ANIM_RESTORE;
+ }
+
+ psys_get_birth_coords(sim, pa, &pa->state, dtime, cfra);
+
+ /* Initialize particle settings which depends on texture.
+ *
+ * We could only do it now because we'll need to know coordinate
+ * before sampling the texture.
+ */
+ initialize_particle_texture(sim, pa, p);
+
+ if (part->phystype==PART_PHYS_BOIDS && pa->boid) {
+ BoidParticle *bpa = pa->boid;
+
+ /* and gravity in r_ve */
+ bpa->gravity[0] = bpa->gravity[1] = 0.0f;
+ bpa->gravity[2] = -1.0f;
+ if ((sim->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) &&
+ (sim->scene->physics_settings.gravity[2] != 0.0f))
+ {
+ bpa->gravity[2] = sim->scene->physics_settings.gravity[2];
+ }
+
+ bpa->data.health = part->boids->health;
+ bpa->data.mode = eBoidMode_InAir;
+ bpa->data.state_id = ((BoidState*)part->boids->states.first)->id;
+ bpa->data.acc[0]=bpa->data.acc[1]=bpa->data.acc[2]=0.0f;
+ }
+
+ if (part->type == PART_HAIR) {
+ pa->lifetime = 100.0f;
+ }
+ else {
+ /* initialize the lifetime, in case the texture coordinates
+ * are from Particles/Strands, which would cause undefined values
+ */
+ pa->lifetime = part->lifetime * (1.0f - part->randlife * psys_frand(psys, p + 21));
+ pa->dietime = pa->time + pa->lifetime;
+
+ /* get possible textural influence */
+ psys_get_texture(sim, pa, &ptex, PAMAP_LIFE, cfra);
+
+ pa->lifetime = part->lifetime * ptex.life;
+
+ if (part->randlife != 0.0f)
+ pa->lifetime *= 1.0f - part->randlife * psys_frand(psys, p + 21);
+ }
+
+ pa->dietime = pa->time + pa->lifetime;
+
+ if (sim->psys->pointcache && sim->psys->pointcache->flag & PTCACHE_BAKED &&
+ sim->psys->pointcache->mem_cache.first) {
+ float dietime = psys_get_dietime_from_cache(sim->psys->pointcache, p);
+ pa->dietime = MIN2(pa->dietime, dietime);
+ }
+
+ if (pa->time > cfra)
+ pa->alive = PARS_UNBORN;
+ else if (pa->dietime <= cfra)
+ pa->alive = PARS_DEAD;
+ else
+ pa->alive = PARS_ALIVE;
+
+ pa->state.time = cfra;
+}
+static void reset_all_particles(ParticleSimulationData *sim, float dtime, float cfra, int from)
+{
+ ParticleData *pa;
+ int p, totpart=sim->psys->totpart;
+
+ for (p=from, pa=sim->psys->particles+from; p<totpart; p++, pa++)
+ reset_particle(sim, pa, dtime, cfra);
+}
+/************************************************/
+/* Particle targets */
+/************************************************/
+ParticleSystem *psys_get_target_system(Object *ob, ParticleTarget *pt)
+{
+ ParticleSystem *psys = NULL;
+
+ if (pt->ob == NULL || pt->ob == ob)
+ psys = BLI_findlink(&ob->particlesystem, pt->psys-1);
+ else
+ psys = BLI_findlink(&pt->ob->particlesystem, pt->psys-1);
+
+ if (psys)
+ pt->flag |= PTARGET_VALID;
+ else
+ pt->flag &= ~PTARGET_VALID;
+
+ return psys;
+}
+/************************************************/
+/* Keyed particles */
+/************************************************/
+/* Counts valid keyed targets */
+void psys_count_keyed_targets(ParticleSimulationData *sim)
+{
+ ParticleSystem *psys = sim->psys, *kpsys;
+ ParticleTarget *pt = psys->targets.first;
+ int keys_valid = 1;
+ psys->totkeyed = 0;
+
+ for (; pt; pt=pt->next) {
+ kpsys = psys_get_target_system(sim->ob, pt);
+
+ if (kpsys && kpsys->totpart) {
+ psys->totkeyed += keys_valid;
+ if (psys->flag & PSYS_KEYED_TIMING && pt->duration != 0.0f)
+ psys->totkeyed += 1;
+ }
+ else {
+ keys_valid = 0;
+ }
+ }
+
+ psys->totkeyed *= psys->flag & PSYS_KEYED_TIMING ? 1 : psys->part->keyed_loops;
+}
+
+static void set_keyed_keys(ParticleSimulationData *sim)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSimulationData ksim= {0};
+ ParticleTarget *pt;
+ PARTICLE_P;
+ ParticleKey *key;
+ int totpart = psys->totpart, k, totkeys = psys->totkeyed;
+ int keyed_flag = 0;
+
+ ksim.scene= sim->scene;
+
+ /* no proper targets so let's clear and bail out */
+ if (psys->totkeyed==0) {
+ free_keyed_keys(psys);
+ psys->flag &= ~PSYS_KEYED;
+ return;
+ }
+
+ if (totpart && psys->particles->totkey != totkeys) {
+ free_keyed_keys(psys);
+
+ key = MEM_callocN(totpart*totkeys*sizeof(ParticleKey), "Keyed keys");
+
+ LOOP_PARTICLES {
+ pa->keys = key;
+ pa->totkey = totkeys;
+ key += totkeys;
+ }
+ }
+
+ psys->flag &= ~PSYS_KEYED;
+
+
+ pt = psys->targets.first;
+ for (k=0; k<totkeys; k++) {
+ ksim.ob = pt->ob ? pt->ob : sim->ob;
+ ksim.psys = BLI_findlink(&ksim.ob->particlesystem, pt->psys - 1);
+ keyed_flag = (ksim.psys->flag & PSYS_KEYED);
+ ksim.psys->flag &= ~PSYS_KEYED;
+
+ LOOP_PARTICLES {
+ key = pa->keys + k;
+ key->time = -1.0; /* use current time */
+
+ psys_get_particle_state(&ksim, p%ksim.psys->totpart, key, 1);
+
+ if (psys->flag & PSYS_KEYED_TIMING) {
+ key->time = pa->time + pt->time;
+ if (pt->duration != 0.0f && k+1 < totkeys) {
+ copy_particle_key(key+1, key, 1);
+ (key+1)->time = pa->time + pt->time + pt->duration;
+ }
+ }
+ else if (totkeys > 1)
+ key->time = pa->time + (float)k / (float)(totkeys - 1) * pa->lifetime;
+ else
+ key->time = pa->time;
+ }
+
+ if (psys->flag & PSYS_KEYED_TIMING && pt->duration!=0.0f)
+ k++;
+
+ ksim.psys->flag |= keyed_flag;
+
+ pt = (pt->next && pt->next->flag & PTARGET_VALID) ? pt->next : psys->targets.first;
+ }
+
+ psys->flag |= PSYS_KEYED;
+}
+
+/************************************************/
+/* Point Cache */
+/************************************************/
+void psys_make_temp_pointcache(Object *ob, ParticleSystem *psys)
+{
+ PointCache *cache = psys->pointcache;
+
+ if (cache->flag & PTCACHE_DISK_CACHE && BLI_listbase_is_empty(&cache->mem_cache)) {
+ PTCacheID pid;
+ BKE_ptcache_id_from_particles(&pid, ob, psys);
+ cache->flag &= ~PTCACHE_DISK_CACHE;
+ BKE_ptcache_disk_to_mem(&pid);
+ cache->flag |= PTCACHE_DISK_CACHE;
+ }
+}
+static void psys_clear_temp_pointcache(ParticleSystem *psys)
+{
+ if (psys->pointcache->flag & PTCACHE_DISK_CACHE)
+ BKE_ptcache_free_mem(&psys->pointcache->mem_cache);
+}
+void psys_get_pointcache_start_end(Scene *scene, ParticleSystem *psys, int *sfra, int *efra)
+{
+ ParticleSettings *part = psys->part;
+
+ *sfra = max_ii(1, (int)part->sta);
+ *efra = min_ii((int)(part->end + part->lifetime + 1.0f), max_ii(scene->r.pefra, scene->r.efra));
+}
+
+/************************************************/
+/* Effectors */
+/************************************************/
+static void psys_update_particle_bvhtree(ParticleSystem *psys, float cfra)
+{
+ if (psys) {
+ PARTICLE_P;
+ int totpart = 0;
+ bool need_rebuild;
+
+ BLI_rw_mutex_lock(&psys_bvhtree_rwlock, THREAD_LOCK_READ);
+ need_rebuild = !psys->bvhtree || psys->bvhtree_frame != cfra;
+ BLI_rw_mutex_unlock(&psys_bvhtree_rwlock);
+
+ if (need_rebuild) {
+ LOOP_SHOWN_PARTICLES {
+ totpart++;
+ }
+
+ BLI_rw_mutex_lock(&psys_bvhtree_rwlock, THREAD_LOCK_WRITE);
+
+ BLI_bvhtree_free(psys->bvhtree);
+ psys->bvhtree = BLI_bvhtree_new(totpart, 0.0, 4, 6);
+
+ LOOP_SHOWN_PARTICLES {
+ if (pa->alive == PARS_ALIVE) {
+ if (pa->state.time == cfra)
+ BLI_bvhtree_insert(psys->bvhtree, p, pa->prev_state.co, 1);
+ else
+ BLI_bvhtree_insert(psys->bvhtree, p, pa->state.co, 1);
+ }
+ }
+ BLI_bvhtree_balance(psys->bvhtree);
+
+ psys->bvhtree_frame = cfra;
+
+ BLI_rw_mutex_unlock(&psys_bvhtree_rwlock);
+ }
+ }
+}
+void psys_update_particle_tree(ParticleSystem *psys, float cfra)
+{
+ if (psys) {
+ PARTICLE_P;
+ int totpart = 0;
+
+ if (!psys->tree || psys->tree_frame != cfra) {
+ LOOP_SHOWN_PARTICLES {
+ totpart++;
+ }
+
+ BLI_kdtree_free(psys->tree);
+ psys->tree = BLI_kdtree_new(psys->totpart);
+
+ LOOP_SHOWN_PARTICLES {
+ if (pa->alive == PARS_ALIVE) {
+ if (pa->state.time == cfra)
+ BLI_kdtree_insert(psys->tree, p, pa->prev_state.co);
+ else
+ BLI_kdtree_insert(psys->tree, p, pa->state.co);
+ }
+ }
+ BLI_kdtree_balance(psys->tree);
+
+ psys->tree_frame = cfra;
+ }
+ }
+}
+
+static void psys_update_effectors(ParticleSimulationData *sim)
+{
+ pdEndEffectors(&sim->psys->effectors);
+ sim->psys->effectors = pdInitEffectors(sim->scene, sim->ob, sim->psys,
+ sim->psys->part->effector_weights, true);
+ precalc_guides(sim, sim->psys->effectors);
+}
+
+static void integrate_particle(ParticleSettings *part, ParticleData *pa, float dtime, float *external_acceleration,
+ void (*force_func)(void *forcedata, ParticleKey *state, float *force, float *impulse),
+ void *forcedata)
+{
+#define ZERO_F43 {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}
+
+ ParticleKey states[5];
+ float force[3], acceleration[3], impulse[3], dx[4][3] = ZERO_F43, dv[4][3] = ZERO_F43, oldpos[3];
+ float pa_mass= (part->flag & PART_SIZEMASS ? part->mass * pa->size : part->mass);
+ int i, steps=1;
+ int integrator = part->integrator;
+
+#undef ZERO_F43
+
+ copy_v3_v3(oldpos, pa->state.co);
+
+ /* Verlet integration behaves strangely with moving emitters, so do first step with euler. */
+ if (pa->prev_state.time < 0.f && integrator == PART_INT_VERLET)
+ integrator = PART_INT_EULER;
+
+ switch (integrator) {
+ case PART_INT_EULER:
+ steps=1;
+ break;
+ case PART_INT_MIDPOINT:
+ steps=2;
+ break;
+ case PART_INT_RK4:
+ steps=4;
+ break;
+ case PART_INT_VERLET:
+ steps=1;
+ break;
+ }
+
+ for (i=0; i<steps; i++) {
+ copy_particle_key(states + i, &pa->state, 1);
+ }
+
+ states->time = 0.f;
+
+ for (i=0; i<steps; i++) {
+ zero_v3(force);
+ zero_v3(impulse);
+
+ force_func(forcedata, states+i, force, impulse);
+
+ /* force to acceleration*/
+ mul_v3_v3fl(acceleration, force, 1.0f/pa_mass);
+
+ if (external_acceleration)
+ add_v3_v3(acceleration, external_acceleration);
+
+ /* calculate next state */
+ add_v3_v3(states[i].vel, impulse);
+
+ switch (integrator) {
+ case PART_INT_EULER:
+ madd_v3_v3v3fl(pa->state.co, states->co, states->vel, dtime);
+ madd_v3_v3v3fl(pa->state.vel, states->vel, acceleration, dtime);
+ break;
+ case PART_INT_MIDPOINT:
+ if (i==0) {
+ madd_v3_v3v3fl(states[1].co, states->co, states->vel, dtime*0.5f);
+ madd_v3_v3v3fl(states[1].vel, states->vel, acceleration, dtime*0.5f);
+ states[1].time = dtime*0.5f;
+ /*fra=sim->psys->cfra+0.5f*dfra;*/
+ }
+ else {
+ madd_v3_v3v3fl(pa->state.co, states->co, states[1].vel, dtime);
+ madd_v3_v3v3fl(pa->state.vel, states->vel, acceleration, dtime);
+ }
+ break;
+ case PART_INT_RK4:
+ switch (i) {
+ case 0:
+ copy_v3_v3(dx[0], states->vel);
+ mul_v3_fl(dx[0], dtime);
+ copy_v3_v3(dv[0], acceleration);
+ mul_v3_fl(dv[0], dtime);
+
+ madd_v3_v3v3fl(states[1].co, states->co, dx[0], 0.5f);
+ madd_v3_v3v3fl(states[1].vel, states->vel, dv[0], 0.5f);
+ states[1].time = dtime*0.5f;
+ /*fra=sim->psys->cfra+0.5f*dfra;*/
+ break;
+ case 1:
+ madd_v3_v3v3fl(dx[1], states->vel, dv[0], 0.5f);
+ mul_v3_fl(dx[1], dtime);
+ copy_v3_v3(dv[1], acceleration);
+ mul_v3_fl(dv[1], dtime);
+
+ madd_v3_v3v3fl(states[2].co, states->co, dx[1], 0.5f);
+ madd_v3_v3v3fl(states[2].vel, states->vel, dv[1], 0.5f);
+ states[2].time = dtime*0.5f;
+ break;
+ case 2:
+ madd_v3_v3v3fl(dx[2], states->vel, dv[1], 0.5f);
+ mul_v3_fl(dx[2], dtime);
+ copy_v3_v3(dv[2], acceleration);
+ mul_v3_fl(dv[2], dtime);
+
+ add_v3_v3v3(states[3].co, states->co, dx[2]);
+ add_v3_v3v3(states[3].vel, states->vel, dv[2]);
+ states[3].time = dtime;
+ /*fra=cfra;*/
+ break;
+ case 3:
+ add_v3_v3v3(dx[3], states->vel, dv[2]);
+ mul_v3_fl(dx[3], dtime);
+ copy_v3_v3(dv[3], acceleration);
+ mul_v3_fl(dv[3], dtime);
+
+ madd_v3_v3v3fl(pa->state.co, states->co, dx[0], 1.0f/6.0f);
+ madd_v3_v3fl(pa->state.co, dx[1], 1.0f/3.0f);
+ madd_v3_v3fl(pa->state.co, dx[2], 1.0f/3.0f);
+ madd_v3_v3fl(pa->state.co, dx[3], 1.0f/6.0f);
+
+ madd_v3_v3v3fl(pa->state.vel, states->vel, dv[0], 1.0f/6.0f);
+ madd_v3_v3fl(pa->state.vel, dv[1], 1.0f/3.0f);
+ madd_v3_v3fl(pa->state.vel, dv[2], 1.0f/3.0f);
+ madd_v3_v3fl(pa->state.vel, dv[3], 1.0f/6.0f);
+ }
+ break;
+ case PART_INT_VERLET: /* Verlet integration */
+ madd_v3_v3v3fl(pa->state.vel, pa->prev_state.vel, acceleration, dtime);
+ madd_v3_v3v3fl(pa->state.co, pa->prev_state.co, pa->state.vel, dtime);
+
+ sub_v3_v3v3(pa->state.vel, pa->state.co, oldpos);
+ mul_v3_fl(pa->state.vel, 1.0f/dtime);
+ break;
+ }
+ }
+}
+
+/*********************************************************************************************************
+ * SPH fluid physics
+ *
+ * In theory, there could be unlimited implementation of SPH simulators
+ *
+ * This code uses in some parts adapted algorithms from the pseudo code as outlined in the Research paper:
+ *
+ * Titled: Particle-based Viscoelastic Fluid Simulation.
+ * Authors: Simon Clavet, Philippe Beaudoin and Pierre Poulin
+ * Website: http://www.iro.umontreal.ca/labs/infographie/papers/Clavet-2005-PVFS/
+ *
+ * Presented at Siggraph, (2005)
+ *
+ * ********************************************************************************************************/
+#define PSYS_FLUID_SPRINGS_INITIAL_SIZE 256
+static ParticleSpring *sph_spring_add(ParticleSystem *psys, ParticleSpring *spring)
+{
+ /* Are more refs required? */
+ if (psys->alloc_fluidsprings == 0 || psys->fluid_springs == NULL) {
+ psys->alloc_fluidsprings = PSYS_FLUID_SPRINGS_INITIAL_SIZE;
+ psys->fluid_springs = (ParticleSpring*)MEM_callocN(psys->alloc_fluidsprings * sizeof(ParticleSpring), "Particle Fluid Springs");
+ }
+ else if (psys->tot_fluidsprings == psys->alloc_fluidsprings) {
+ /* Double the number of refs allocated */
+ psys->alloc_fluidsprings *= 2;
+ psys->fluid_springs = (ParticleSpring*)MEM_reallocN(psys->fluid_springs, psys->alloc_fluidsprings * sizeof(ParticleSpring));
+ }
+
+ memcpy(psys->fluid_springs + psys->tot_fluidsprings, spring, sizeof(ParticleSpring));
+ psys->tot_fluidsprings++;
+
+ return psys->fluid_springs + psys->tot_fluidsprings - 1;
+}
+static void sph_spring_delete(ParticleSystem *psys, int j)
+{
+ if (j != psys->tot_fluidsprings - 1)
+ psys->fluid_springs[j] = psys->fluid_springs[psys->tot_fluidsprings - 1];
+
+ psys->tot_fluidsprings--;
+
+ if (psys->tot_fluidsprings < psys->alloc_fluidsprings/2 && psys->alloc_fluidsprings > PSYS_FLUID_SPRINGS_INITIAL_SIZE) {
+ psys->alloc_fluidsprings /= 2;
+ psys->fluid_springs = (ParticleSpring*)MEM_reallocN(psys->fluid_springs, psys->alloc_fluidsprings * sizeof(ParticleSpring));
+ }
+}
+static void sph_springs_modify(ParticleSystem *psys, float dtime)
+{
+ SPHFluidSettings *fluid = psys->part->fluid;
+ ParticleData *pa1, *pa2;
+ ParticleSpring *spring = psys->fluid_springs;
+
+ float h, d, Rij[3], rij, Lij;
+ int i;
+
+ float yield_ratio = fluid->yield_ratio;
+ float plasticity = fluid->plasticity_constant;
+ /* scale things according to dtime */
+ float timefix = 25.f * dtime;
+
+ if ((fluid->flag & SPH_VISCOELASTIC_SPRINGS)==0 || fluid->spring_k == 0.f)
+ return;
+
+ /* Loop through the springs */
+ for (i=0; i<psys->tot_fluidsprings; i++, spring++) {
+ pa1 = psys->particles + spring->particle_index[0];
+ pa2 = psys->particles + spring->particle_index[1];
+
+ sub_v3_v3v3(Rij, pa2->prev_state.co, pa1->prev_state.co);
+ rij = normalize_v3(Rij);
+
+ /* adjust rest length */
+ Lij = spring->rest_length;
+ d = yield_ratio * timefix * Lij;
+
+ if (rij > Lij + d) // Stretch
+ spring->rest_length += plasticity * (rij - Lij - d) * timefix;
+ else if (rij < Lij - d) // Compress
+ spring->rest_length -= plasticity * (Lij - d - rij) * timefix;
+
+ h = 4.f*pa1->size;
+
+ if (spring->rest_length > h)
+ spring->delete_flag = 1;
+ }
+
+ /* Loop through springs backwaqrds - for efficient delete function */
+ for (i=psys->tot_fluidsprings-1; i >= 0; i--) {
+ if (psys->fluid_springs[i].delete_flag)
+ sph_spring_delete(psys, i);
+ }
+}
+static EdgeHash *sph_springhash_build(ParticleSystem *psys)
+{
+ EdgeHash *springhash = NULL;
+ ParticleSpring *spring;
+ int i = 0;
+
+ springhash = BLI_edgehash_new_ex(__func__, psys->tot_fluidsprings);
+
+ for (i=0, spring=psys->fluid_springs; i<psys->tot_fluidsprings; i++, spring++)
+ BLI_edgehash_insert(springhash, spring->particle_index[0], spring->particle_index[1], SET_INT_IN_POINTER(i+1));
+
+ return springhash;
+}
+
+#define SPH_NEIGHBORS 512
+typedef struct SPHNeighbor {
+ ParticleSystem *psys;
+ int index;
+} SPHNeighbor;
+
+typedef struct SPHRangeData {
+ SPHNeighbor neighbors[SPH_NEIGHBORS];
+ int tot_neighbors;
+
+ float* data;
+
+ ParticleSystem *npsys;
+ ParticleData *pa;
+
+ float h;
+ float mass;
+ float massfac;
+ int use_size;
+} SPHRangeData;
+
+static void sph_evaluate_func(BVHTree *tree, ParticleSystem **psys, float co[3], SPHRangeData *pfr, float interaction_radius, BVHTree_RangeQuery callback)
+{
+ int i;
+
+ pfr->tot_neighbors = 0;
+
+ for (i=0; i < 10 && psys[i]; i++) {
+ pfr->npsys = psys[i];
+ pfr->massfac = psys[i]->part->mass / pfr->mass;
+ pfr->use_size = psys[i]->part->flag & PART_SIZEMASS;
+
+ if (tree) {
+ BLI_bvhtree_range_query(tree, co, interaction_radius, callback, pfr);
+ break;
+ }
+ else {
+ BLI_rw_mutex_lock(&psys_bvhtree_rwlock, THREAD_LOCK_READ);
+
+ BLI_bvhtree_range_query(psys[i]->bvhtree, co, interaction_radius, callback, pfr);
+
+ BLI_rw_mutex_unlock(&psys_bvhtree_rwlock);
+ }
+ }
+}
+static void sph_density_accum_cb(void *userdata, int index, const float co[3], float squared_dist)
+{
+ SPHRangeData *pfr = (SPHRangeData *)userdata;
+ ParticleData *npa = pfr->npsys->particles + index;
+ float q;
+ float dist;
+
+ UNUSED_VARS(co);
+
+ if (npa == pfr->pa || squared_dist < FLT_EPSILON)
+ return;
+
+ /* Ugh! One particle has too many neighbors! If some aren't taken into
+ * account, the forces will be biased by the tree search order. This
+ * effectively adds enery to the system, and results in a churning motion.
+ * But, we have to stop somewhere, and it's not the end of the world.
+ * - jahka and z0r
+ */
+ if (pfr->tot_neighbors >= SPH_NEIGHBORS)
+ return;
+
+ pfr->neighbors[pfr->tot_neighbors].index = index;
+ pfr->neighbors[pfr->tot_neighbors].psys = pfr->npsys;
+ pfr->tot_neighbors++;
+
+ dist = sqrtf(squared_dist);
+ q = (1.f - dist/pfr->h) * pfr->massfac;
+
+ if (pfr->use_size)
+ q *= npa->size;
+
+ pfr->data[0] += q*q;
+ pfr->data[1] += q*q*q;
+}
+
+/*
+ * Find the Courant number for an SPH particle (used for adaptive time step).
+ */
+static void sph_particle_courant(SPHData *sphdata, SPHRangeData *pfr)
+{
+ ParticleData *pa, *npa;
+ int i;
+ float flow[3], offset[3], dist;
+
+ zero_v3(flow);
+
+ dist = 0.0f;
+ if (pfr->tot_neighbors > 0) {
+ pa = pfr->pa;
+ for (i=0; i < pfr->tot_neighbors; i++) {
+ npa = pfr->neighbors[i].psys->particles + pfr->neighbors[i].index;
+ sub_v3_v3v3(offset, pa->prev_state.co, npa->prev_state.co);
+ dist += len_v3(offset);
+ add_v3_v3(flow, npa->prev_state.vel);
+ }
+ dist += sphdata->psys[0]->part->fluid->radius; // TODO: remove this? - z0r
+ sphdata->element_size = dist / pfr->tot_neighbors;
+ mul_v3_v3fl(sphdata->flow, flow, 1.0f / pfr->tot_neighbors);
+ }
+ else {
+ sphdata->element_size = FLT_MAX;
+ copy_v3_v3(sphdata->flow, flow);
+ }
+}
+static void sph_force_cb(void *sphdata_v, ParticleKey *state, float *force, float *UNUSED(impulse))
+{
+ SPHData *sphdata = (SPHData *)sphdata_v;
+ ParticleSystem **psys = sphdata->psys;
+ ParticleData *pa = sphdata->pa;
+ SPHFluidSettings *fluid = psys[0]->part->fluid;
+ ParticleSpring *spring = NULL;
+ SPHRangeData pfr;
+ SPHNeighbor *pfn;
+ float *gravity = sphdata->gravity;
+ EdgeHash *springhash = sphdata->eh;
+
+ float q, u, rij, dv[3];
+ float pressure, near_pressure;
+
+ float visc = fluid->viscosity_omega;
+ float stiff_visc = fluid->viscosity_beta * (fluid->flag & SPH_FAC_VISCOSITY ? fluid->viscosity_omega : 1.f);
+
+ float inv_mass = 1.0f / sphdata->mass;
+ float spring_constant = fluid->spring_k;
+
+ /* 4.0 seems to be a pretty good value */
+ float interaction_radius = fluid->radius * (fluid->flag & SPH_FAC_RADIUS ? 4.0f * pa->size : 1.0f);
+ float h = interaction_radius * sphdata->hfac;
+ float rest_density = fluid->rest_density * (fluid->flag & SPH_FAC_DENSITY ? 4.77f : 1.f); /* 4.77 is an experimentally determined density factor */
+ float rest_length = fluid->rest_length * (fluid->flag & SPH_FAC_REST_LENGTH ? 2.588f * pa->size : 1.f);
+
+ float stiffness = fluid->stiffness_k;
+ float stiffness_near_fac = fluid->stiffness_knear * (fluid->flag & SPH_FAC_REPULSION ? fluid->stiffness_k : 1.f);
+
+ ParticleData *npa;
+ float vec[3];
+ float vel[3];
+ float co[3];
+ float data[2];
+ float density, near_density;
+
+ int i, spring_index, index = pa - psys[0]->particles;
+
+ data[0] = data[1] = 0;
+ pfr.data = data;
+ pfr.h = h;
+ pfr.pa = pa;
+ pfr.mass = sphdata->mass;
+
+ sph_evaluate_func( NULL, psys, state->co, &pfr, interaction_radius, sph_density_accum_cb);
+
+ density = data[0];
+ near_density = data[1];
+
+ pressure = stiffness * (density - rest_density);
+ near_pressure = stiffness_near_fac * near_density;
+
+ pfn = pfr.neighbors;
+ for (i=0; i<pfr.tot_neighbors; i++, pfn++) {
+ npa = pfn->psys->particles + pfn->index;
+
+ madd_v3_v3v3fl(co, npa->prev_state.co, npa->prev_state.vel, state->time);
+
+ sub_v3_v3v3(vec, co, state->co);
+ rij = normalize_v3(vec);
+
+ q = (1.f - rij/h) * pfn->psys->part->mass * inv_mass;
+
+ if (pfn->psys->part->flag & PART_SIZEMASS)
+ q *= npa->size;
+
+ copy_v3_v3(vel, npa->prev_state.vel);
+
+ /* Double Density Relaxation */
+ madd_v3_v3fl(force, vec, -(pressure + near_pressure*q)*q);
+
+ /* Viscosity */
+ if (visc > 0.f || stiff_visc > 0.f) {
+ sub_v3_v3v3(dv, vel, state->vel);
+ u = dot_v3v3(vec, dv);
+
+ if (u < 0.f && visc > 0.f)
+ madd_v3_v3fl(force, vec, 0.5f * q * visc * u );
+
+ if (u > 0.f && stiff_visc > 0.f)
+ madd_v3_v3fl(force, vec, 0.5f * q * stiff_visc * u );
+ }
+
+ if (spring_constant > 0.f) {
+ /* Viscoelastic spring force */
+ if (pfn->psys == psys[0] && fluid->flag & SPH_VISCOELASTIC_SPRINGS && springhash) {
+ /* BLI_edgehash_lookup appears to be thread-safe. - z0r */
+ spring_index = GET_INT_FROM_POINTER(BLI_edgehash_lookup(springhash, index, pfn->index));
+
+ if (spring_index) {
+ spring = psys[0]->fluid_springs + spring_index - 1;
+
+ madd_v3_v3fl(force, vec, -10.f * spring_constant * (1.f - rij/h) * (spring->rest_length - rij));
+ }
+ else if (fluid->spring_frames == 0 || (pa->prev_state.time-pa->time) <= fluid->spring_frames) {
+ ParticleSpring temp_spring;
+ temp_spring.particle_index[0] = index;
+ temp_spring.particle_index[1] = pfn->index;
+ temp_spring.rest_length = (fluid->flag & SPH_CURRENT_REST_LENGTH) ? rij : rest_length;
+ temp_spring.delete_flag = 0;
+
+ /* sph_spring_add is not thread-safe. - z0r */
+ sph_spring_add(psys[0], &temp_spring);
+ }
+ }
+ else {/* PART_SPRING_HOOKES - Hooke's spring force */
+ madd_v3_v3fl(force, vec, -10.f * spring_constant * (1.f - rij/h) * (rest_length - rij));
+ }
+ }
+ }
+
+ /* Artificial buoyancy force in negative gravity direction */
+ if (fluid->buoyancy > 0.f && gravity)
+ madd_v3_v3fl(force, gravity, fluid->buoyancy * (density-rest_density));
+
+ if (sphdata->pass == 0 && psys[0]->part->time_flag & PART_TIME_AUTOSF)
+ sph_particle_courant(sphdata, &pfr);
+ sphdata->pass++;
+}
+
+static void sphclassical_density_accum_cb(void *userdata, int index, const float co[3], float UNUSED(squared_dist))
+{
+ SPHRangeData *pfr = (SPHRangeData *)userdata;
+ ParticleData *npa = pfr->npsys->particles + index;
+ float q;
+ float qfac = 21.0f / (256.f * (float)M_PI);
+ float rij, rij_h;
+ float vec[3];
+
+ /* Exclude particles that are more than 2h away. Can't use squared_dist here
+ * because it is not accurate enough. Use current state, i.e. the output of
+ * basic_integrate() - z0r */
+ sub_v3_v3v3(vec, npa->state.co, co);
+ rij = len_v3(vec);
+ rij_h = rij / pfr->h;
+ if (rij_h > 2.0f)
+ return;
+
+ /* Smoothing factor. Utilise the Wendland kernel. gnuplot:
+ * q1(x) = (2.0 - x)**4 * ( 1.0 + 2.0 * x)
+ * plot [0:2] q1(x) */
+ q = qfac / pow3f(pfr->h) * pow4f(2.0f - rij_h) * ( 1.0f + 2.0f * rij_h);
+ q *= pfr->npsys->part->mass;
+
+ if (pfr->use_size)
+ q *= pfr->pa->size;
+
+ pfr->data[0] += q;
+ pfr->data[1] += q / npa->sphdensity;
+}
+
+static void sphclassical_neighbour_accum_cb(void *userdata, int index, const float co[3], float UNUSED(squared_dist))
+{
+ SPHRangeData *pfr = (SPHRangeData *)userdata;
+ ParticleData *npa = pfr->npsys->particles + index;
+ float rij, rij_h;
+ float vec[3];
+
+ if (pfr->tot_neighbors >= SPH_NEIGHBORS)
+ return;
+
+ /* Exclude particles that are more than 2h away. Can't use squared_dist here
+ * because it is not accurate enough. Use current state, i.e. the output of
+ * basic_integrate() - z0r */
+ sub_v3_v3v3(vec, npa->state.co, co);
+ rij = len_v3(vec);
+ rij_h = rij / pfr->h;
+ if (rij_h > 2.0f)
+ return;
+
+ pfr->neighbors[pfr->tot_neighbors].index = index;
+ pfr->neighbors[pfr->tot_neighbors].psys = pfr->npsys;
+ pfr->tot_neighbors++;
+}
+static void sphclassical_force_cb(void *sphdata_v, ParticleKey *state, float *force, float *UNUSED(impulse))
+{
+ SPHData *sphdata = (SPHData *)sphdata_v;
+ ParticleSystem **psys = sphdata->psys;
+ ParticleData *pa = sphdata->pa;
+ SPHFluidSettings *fluid = psys[0]->part->fluid;
+ SPHRangeData pfr;
+ SPHNeighbor *pfn;
+ float *gravity = sphdata->gravity;
+
+ float dq, u, rij, dv[3];
+ float pressure, npressure;
+
+ float visc = fluid->viscosity_omega;
+
+ float interaction_radius;
+ float h, hinv;
+ /* 4.77 is an experimentally determined density factor */
+ float rest_density = fluid->rest_density * (fluid->flag & SPH_FAC_DENSITY ? 4.77f : 1.0f);
+
+ // Use speed of sound squared
+ float stiffness = pow2f(fluid->stiffness_k);
+
+ ParticleData *npa;
+ float vec[3];
+ float co[3];
+ float pressureTerm;
+
+ int i;
+
+ float qfac2 = 42.0f / (256.0f * (float)M_PI);
+ float rij_h;
+
+ /* 4.0 here is to be consistent with previous formulation/interface */
+ interaction_radius = fluid->radius * (fluid->flag & SPH_FAC_RADIUS ? 4.0f * pa->size : 1.0f);
+ h = interaction_radius * sphdata->hfac;
+ hinv = 1.0f / h;
+
+ pfr.h = h;
+ pfr.pa = pa;
+
+ sph_evaluate_func(NULL, psys, state->co, &pfr, interaction_radius, sphclassical_neighbour_accum_cb);
+ pressure = stiffness * (pow7f(pa->sphdensity / rest_density) - 1.0f);
+
+ /* multiply by mass so that we return a force, not accel */
+ qfac2 *= sphdata->mass / pow3f(pfr.h);
+
+ pfn = pfr.neighbors;
+ for (i = 0; i < pfr.tot_neighbors; i++, pfn++) {
+ npa = pfn->psys->particles + pfn->index;
+ if (npa == pa) {
+ /* we do not contribute to ourselves */
+ continue;
+ }
+
+ /* Find vector to neighbor. Exclude particles that are more than 2h
+ * away. Can't use current state here because it may have changed on
+ * another thread - so do own mini integration. Unlike basic_integrate,
+ * SPH integration depends on neighboring particles. - z0r */
+ madd_v3_v3v3fl(co, npa->prev_state.co, npa->prev_state.vel, state->time);
+ sub_v3_v3v3(vec, co, state->co);
+ rij = normalize_v3(vec);
+ rij_h = rij / pfr.h;
+ if (rij_h > 2.0f)
+ continue;
+
+ npressure = stiffness * (pow7f(npa->sphdensity / rest_density) - 1.0f);
+
+ /* First derivative of smoothing factor. Utilise the Wendland kernel.
+ * gnuplot:
+ * q2(x) = 2.0 * (2.0 - x)**4 - 4.0 * (2.0 - x)**3 * (1.0 + 2.0 * x)
+ * plot [0:2] q2(x)
+ * Particles > 2h away are excluded above. */
+ dq = qfac2 * (2.0f * pow4f(2.0f - rij_h) - 4.0f * pow3f(2.0f - rij_h) * (1.0f + 2.0f * rij_h) );
+
+ if (pfn->psys->part->flag & PART_SIZEMASS)
+ dq *= npa->size;
+
+ pressureTerm = pressure / pow2f(pa->sphdensity) + npressure / pow2f(npa->sphdensity);
+
+ /* Note that 'minus' is removed, because vec = vecBA, not vecAB.
+ * This applies to the viscosity calculation below, too. */
+ madd_v3_v3fl(force, vec, pressureTerm * dq);
+
+ /* Viscosity */
+ if (visc > 0.0f) {
+ sub_v3_v3v3(dv, npa->prev_state.vel, pa->prev_state.vel);
+ u = dot_v3v3(vec, dv);
+ /* Apply parameters */
+ u *= -dq * hinv * visc / (0.5f * npa->sphdensity + 0.5f * pa->sphdensity);
+ madd_v3_v3fl(force, vec, u);
+ }
+ }
+
+ /* Artificial buoyancy force in negative gravity direction */
+ if (fluid->buoyancy > 0.f && gravity)
+ madd_v3_v3fl(force, gravity, fluid->buoyancy * (pa->sphdensity - rest_density));
+
+ if (sphdata->pass == 0 && psys[0]->part->time_flag & PART_TIME_AUTOSF)
+ sph_particle_courant(sphdata, &pfr);
+ sphdata->pass++;
+}
+
+static void sphclassical_calc_dens(ParticleData *pa, float UNUSED(dfra), SPHData *sphdata)
+{
+ ParticleSystem **psys = sphdata->psys;
+ SPHFluidSettings *fluid = psys[0]->part->fluid;
+ /* 4.0 seems to be a pretty good value */
+ float interaction_radius = fluid->radius * (fluid->flag & SPH_FAC_RADIUS ? 4.0f * psys[0]->part->size : 1.0f);
+ SPHRangeData pfr;
+ float data[2];
+
+ data[0] = 0;
+ data[1] = 0;
+ pfr.data = data;
+ pfr.h = interaction_radius * sphdata->hfac;
+ pfr.pa = pa;
+ pfr.mass = sphdata->mass;
+
+ sph_evaluate_func( NULL, psys, pa->state.co, &pfr, interaction_radius, sphclassical_density_accum_cb);
+ pa->sphdensity = min_ff(max_ff(data[0], fluid->rest_density * 0.9f), fluid->rest_density * 1.1f);
+}
+
+void psys_sph_init(ParticleSimulationData *sim, SPHData *sphdata)
+{
+ ParticleTarget *pt;
+ int i;
+
+ // Add other coupled particle systems.
+ sphdata->psys[0] = sim->psys;
+ for (i=1, pt=sim->psys->targets.first; i<10; i++, pt=(pt?pt->next:NULL))
+ sphdata->psys[i] = pt ? psys_get_target_system(sim->ob, pt) : NULL;
+
+ if (psys_uses_gravity(sim))
+ sphdata->gravity = sim->scene->physics_settings.gravity;
+ else
+ sphdata->gravity = NULL;
+ sphdata->eh = sph_springhash_build(sim->psys);
+
+ // These per-particle values should be overridden later, but just for
+ // completeness we give them default values now.
+ sphdata->pa = NULL;
+ sphdata->mass = 1.0f;
+
+ if (sim->psys->part->fluid->solver == SPH_SOLVER_DDR) {
+ sphdata->force_cb = sph_force_cb;
+ sphdata->density_cb = sph_density_accum_cb;
+ sphdata->hfac = 1.0f;
+ }
+ else {
+ /* SPH_SOLVER_CLASSICAL */
+ sphdata->force_cb = sphclassical_force_cb;
+ sphdata->density_cb = sphclassical_density_accum_cb;
+ sphdata->hfac = 0.5f;
+ }
+
+}
+
+void psys_sph_finalise(SPHData *sphdata)
+{
+ if (sphdata->eh) {
+ BLI_edgehash_free(sphdata->eh, NULL);
+ sphdata->eh = NULL;
+ }
+}
+/* Sample the density field at a point in space. */
+void psys_sph_density(BVHTree *tree, SPHData *sphdata, float co[3], float vars[2])
+{
+ ParticleSystem **psys = sphdata->psys;
+ SPHFluidSettings *fluid = psys[0]->part->fluid;
+ /* 4.0 seems to be a pretty good value */
+ float interaction_radius = fluid->radius * (fluid->flag & SPH_FAC_RADIUS ? 4.0f * psys[0]->part->size : 1.0f);
+ SPHRangeData pfr;
+ float density[2];
+
+ density[0] = density[1] = 0.0f;
+ pfr.data = density;
+ pfr.h = interaction_radius * sphdata->hfac;
+ pfr.mass = sphdata->mass;
+
+ sph_evaluate_func(tree, psys, co, &pfr, interaction_radius, sphdata->density_cb);
+
+ vars[0] = pfr.data[0];
+ vars[1] = pfr.data[1];
+}
+
+static void sph_integrate(ParticleSimulationData *sim, ParticleData *pa, float dfra, SPHData *sphdata)
+{
+ ParticleSettings *part = sim->psys->part;
+ // float timestep = psys_get_timestep(sim); // UNUSED
+ float pa_mass = part->mass * (part->flag & PART_SIZEMASS ? pa->size : 1.f);
+ float dtime = dfra*psys_get_timestep(sim);
+ // int steps = 1; // UNUSED
+ float effector_acceleration[3];
+
+ sphdata->pa = pa;
+ sphdata->mass = pa_mass;
+ sphdata->pass = 0;
+ //sphdata.element_size and sphdata.flow are set in the callback.
+
+ /* restore previous state and treat gravity & effectors as external acceleration*/
+ sub_v3_v3v3(effector_acceleration, pa->state.vel, pa->prev_state.vel);
+ mul_v3_fl(effector_acceleration, 1.f/dtime);
+
+ copy_particle_key(&pa->state, &pa->prev_state, 0);
+
+ integrate_particle(part, pa, dtime, effector_acceleration, sphdata->force_cb, sphdata);
+}
+
+/************************************************/
+/* Basic physics */
+/************************************************/
+typedef struct EfData {
+ ParticleTexture ptex;
+ ParticleSimulationData *sim;
+ ParticleData *pa;
+} EfData;
+static void basic_force_cb(void *efdata_v, ParticleKey *state, float *force, float *impulse)
+{
+ EfData *efdata = (EfData *)efdata_v;
+ ParticleSimulationData *sim = efdata->sim;
+ ParticleSettings *part = sim->psys->part;
+ ParticleData *pa = efdata->pa;
+ EffectedPoint epoint;
+
+ /* add effectors */
+ pd_point_from_particle(efdata->sim, efdata->pa, state, &epoint);
+ if (part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR)
+ pdDoEffectors(sim->psys->effectors, sim->colliders, part->effector_weights, &epoint, force, impulse);
+
+ mul_v3_fl(force, efdata->ptex.field);
+ mul_v3_fl(impulse, efdata->ptex.field);
+
+ /* calculate air-particle interaction */
+ if (part->dragfac != 0.0f)
+ madd_v3_v3fl(force, state->vel, -part->dragfac * pa->size * pa->size * len_v3(state->vel));
+
+ /* brownian force */
+ if (part->brownfac != 0.0f) {
+ force[0] += (BLI_frand()-0.5f) * part->brownfac;
+ force[1] += (BLI_frand()-0.5f) * part->brownfac;
+ force[2] += (BLI_frand()-0.5f) * part->brownfac;
+ }
+
+ if (part->flag & PART_ROT_DYN && epoint.ave)
+ copy_v3_v3(pa->state.ave, epoint.ave);
+}
+/* gathers all forces that effect particles and calculates a new state for the particle */
+static void basic_integrate(ParticleSimulationData *sim, int p, float dfra, float cfra)
+{
+ ParticleSettings *part = sim->psys->part;
+ ParticleData *pa = sim->psys->particles + p;
+ ParticleKey tkey;
+ float dtime=dfra*psys_get_timestep(sim), time;
+ float *gravity = NULL, gr[3];
+ EfData efdata;
+
+ psys_get_texture(sim, pa, &efdata.ptex, PAMAP_PHYSICS, cfra);
+
+ efdata.pa = pa;
+ efdata.sim = sim;
+
+ /* add global acceleration (gravitation) */
+ if (psys_uses_gravity(sim) &&
+ /* normal gravity is too strong for hair so it's disabled by default */
+ (part->type != PART_HAIR || part->effector_weights->flag & EFF_WEIGHT_DO_HAIR))
+ {
+ zero_v3(gr);
+ madd_v3_v3fl(gr, sim->scene->physics_settings.gravity, part->effector_weights->global_gravity * efdata.ptex.gravity);
+ gravity = gr;
+ }
+
+ /* maintain angular velocity */
+ copy_v3_v3(pa->state.ave, pa->prev_state.ave);
+
+ integrate_particle(part, pa, dtime, gravity, basic_force_cb, &efdata);
+
+ /* damp affects final velocity */
+ if (part->dampfac != 0.f)
+ mul_v3_fl(pa->state.vel, 1.f - part->dampfac * efdata.ptex.damp * 25.f * dtime);
+
+ //copy_v3_v3(pa->state.ave, states->ave);
+
+ /* finally we do guides */
+ time=(cfra-pa->time)/pa->lifetime;
+ CLAMP(time, 0.0f, 1.0f);
+
+ copy_v3_v3(tkey.co,pa->state.co);
+ copy_v3_v3(tkey.vel,pa->state.vel);
+ tkey.time=pa->state.time;
+
+ if (part->type != PART_HAIR) {
+ if (do_guides(sim->psys->part, sim->psys->effectors, &tkey, p, time)) {
+ copy_v3_v3(pa->state.co,tkey.co);
+ /* guides don't produce valid velocity */
+ sub_v3_v3v3(pa->state.vel, tkey.co, pa->prev_state.co);
+ mul_v3_fl(pa->state.vel,1.0f/dtime);
+ pa->state.time=tkey.time;
+ }
+ }
+}
+static void basic_rotate(ParticleSettings *part, ParticleData *pa, float dfra, float timestep)
+{
+ float rotfac, rot1[4], rot2[4] = {1.0,0.0,0.0,0.0}, dtime=dfra*timestep, extrotfac;
+
+ if ((part->flag & PART_ROTATIONS) == 0) {
+ unit_qt(pa->state.rot);
+ return;
+ }
+
+ if (part->flag & PART_ROT_DYN) {
+ extrotfac = len_v3(pa->state.ave);
+ }
+ else {
+ extrotfac = 0.0f;
+ }
+
+ if ((part->flag & PART_ROT_DYN) && ELEM(part->avemode, PART_AVE_VELOCITY, PART_AVE_HORIZONTAL, PART_AVE_VERTICAL)) {
+ float angle;
+ float len1 = len_v3(pa->prev_state.vel);
+ float len2 = len_v3(pa->state.vel);
+ float vec[3];
+
+ if (len1 == 0.0f || len2 == 0.0f) {
+ zero_v3(pa->state.ave);
+ }
+ else {
+ cross_v3_v3v3(pa->state.ave, pa->prev_state.vel, pa->state.vel);
+ normalize_v3(pa->state.ave);
+ angle = dot_v3v3(pa->prev_state.vel, pa->state.vel) / (len1 * len2);
+ mul_v3_fl(pa->state.ave, saacos(angle) / dtime);
+ }
+
+ get_angular_velocity_vector(part->avemode, &pa->state, vec);
+ axis_angle_to_quat(rot2, vec, dtime*part->avefac);
+ }
+
+ rotfac = len_v3(pa->state.ave);
+ if (rotfac == 0.0f || (part->flag & PART_ROT_DYN)==0 || extrotfac == 0.0f) {
+ unit_qt(rot1);
+ }
+ else {
+ axis_angle_to_quat(rot1,pa->state.ave,rotfac*dtime);
+ }
+ mul_qt_qtqt(pa->state.rot,rot1,pa->prev_state.rot);
+ mul_qt_qtqt(pa->state.rot,rot2,pa->state.rot);
+
+ /* keep rotation quat in good health */
+ normalize_qt(pa->state.rot);
+}
+
+/************************************************
+ * Collisions
+ *
+ * The algorithm is roughly:
+ * 1. Use a BVH tree to search for faces that a particle may collide with.
+ * 2. Use Newton's method to find the exact time at which the collision occurs.
+ * https://en.wikipedia.org/wiki/Newton's_method
+ *
+ ************************************************/
+#define COLLISION_MIN_RADIUS 0.001f
+#define COLLISION_MIN_DISTANCE 0.0001f
+#define COLLISION_ZERO 0.00001f
+#define COLLISION_INIT_STEP 0.00008f
+typedef float (*NRDistanceFunc)(float *p, float radius, ParticleCollisionElement *pce, float *nor);
+static float nr_signed_distance_to_plane(float *p, float radius, ParticleCollisionElement *pce, float *nor)
+{
+ float p0[3], e1[3], e2[3], d;
+
+ sub_v3_v3v3(e1, pce->x1, pce->x0);
+ sub_v3_v3v3(e2, pce->x2, pce->x0);
+ sub_v3_v3v3(p0, p, pce->x0);
+
+ cross_v3_v3v3(nor, e1, e2);
+ normalize_v3(nor);
+
+ d = dot_v3v3(p0, nor);
+
+ if (pce->inv_nor == -1) {
+ if (d < 0.f)
+ pce->inv_nor = 1;
+ else
+ pce->inv_nor = 0;
+ }
+
+ if (pce->inv_nor == 1) {
+ negate_v3(nor);
+ d = -d;
+ }
+
+ return d - radius;
+}
+static float nr_distance_to_edge(float *p, float radius, ParticleCollisionElement *pce, float *UNUSED(nor))
+{
+ float v0[3], v1[3], v2[3], c[3];
+
+ sub_v3_v3v3(v0, pce->x1, pce->x0);
+ sub_v3_v3v3(v1, p, pce->x0);
+ sub_v3_v3v3(v2, p, pce->x1);
+
+ cross_v3_v3v3(c, v1, v2);
+
+ return fabsf(len_v3(c)/len_v3(v0)) - radius;
+}
+static float nr_distance_to_vert(float *p, float radius, ParticleCollisionElement *pce, float *UNUSED(nor))
+{
+ return len_v3v3(p, pce->x0) - radius;
+}
+static void collision_interpolate_element(ParticleCollisionElement *pce, float t, float fac, ParticleCollision *col)
+{
+ /* t is the current time for newton rhapson */
+ /* fac is the starting factor for current collision iteration */
+ /* the col->fac's are factors for the particle subframe step start and end during collision modifier step */
+ float f = fac + t*(1.f-fac);
+ float mul = col->fac1 + f * (col->fac2-col->fac1);
+ if (pce->tot > 0) {
+ madd_v3_v3v3fl(pce->x0, pce->x[0], pce->v[0], mul);
+
+ if (pce->tot > 1) {
+ madd_v3_v3v3fl(pce->x1, pce->x[1], pce->v[1], mul);
+
+ if (pce->tot > 2)
+ madd_v3_v3v3fl(pce->x2, pce->x[2], pce->v[2], mul);
+ }
+ }
+}
+static void collision_point_velocity(ParticleCollisionElement *pce)
+{
+ float v[3];
+
+ copy_v3_v3(pce->vel, pce->v[0]);
+
+ if (pce->tot > 1) {
+ sub_v3_v3v3(v, pce->v[1], pce->v[0]);
+ madd_v3_v3fl(pce->vel, v, pce->uv[0]);
+
+ if (pce->tot > 2) {
+ sub_v3_v3v3(v, pce->v[2], pce->v[0]);
+ madd_v3_v3fl(pce->vel, v, pce->uv[1]);
+ }
+ }
+}
+static float collision_point_distance_with_normal(float p[3], ParticleCollisionElement *pce, float fac, ParticleCollision *col, float *nor)
+{
+ if (fac >= 0.f)
+ collision_interpolate_element(pce, 0.f, fac, col);
+
+ switch (pce->tot) {
+ case 1:
+ {
+ sub_v3_v3v3(nor, p, pce->x0);
+ return normalize_v3(nor);
+ }
+ case 2:
+ {
+ float u, e[3], vec[3];
+ sub_v3_v3v3(e, pce->x1, pce->x0);
+ sub_v3_v3v3(vec, p, pce->x0);
+ u = dot_v3v3(vec, e) / dot_v3v3(e, e);
+
+ madd_v3_v3v3fl(nor, vec, e, -u);
+ return normalize_v3(nor);
+ }
+ case 3:
+ return nr_signed_distance_to_plane(p, 0.f, pce, nor);
+ }
+ return 0;
+}
+static void collision_point_on_surface(float p[3], ParticleCollisionElement *pce, float fac, ParticleCollision *col, float *co)
+{
+ collision_interpolate_element(pce, 0.f, fac, col);
+
+ switch (pce->tot) {
+ case 1:
+ {
+ sub_v3_v3v3(co, p, pce->x0);
+ normalize_v3(co);
+ madd_v3_v3v3fl(co, pce->x0, co, col->radius);
+ break;
+ }
+ case 2:
+ {
+ float u, e[3], vec[3], nor[3];
+ sub_v3_v3v3(e, pce->x1, pce->x0);
+ sub_v3_v3v3(vec, p, pce->x0);
+ u = dot_v3v3(vec, e) / dot_v3v3(e, e);
+
+ madd_v3_v3v3fl(nor, vec, e, -u);
+ normalize_v3(nor);
+
+ madd_v3_v3v3fl(co, pce->x0, e, pce->uv[0]);
+ madd_v3_v3fl(co, nor, col->radius);
+ break;
+ }
+ case 3:
+ {
+ float p0[3], e1[3], e2[3], nor[3];
+
+ sub_v3_v3v3(e1, pce->x1, pce->x0);
+ sub_v3_v3v3(e2, pce->x2, pce->x0);
+ sub_v3_v3v3(p0, p, pce->x0);
+
+ cross_v3_v3v3(nor, e1, e2);
+ normalize_v3(nor);
+
+ if (pce->inv_nor == 1)
+ negate_v3(nor);
+
+ madd_v3_v3v3fl(co, pce->x0, nor, col->radius);
+ madd_v3_v3fl(co, e1, pce->uv[0]);
+ madd_v3_v3fl(co, e2, pce->uv[1]);
+ break;
+ }
+ }
+}
+/* find first root in range [0-1] starting from 0 */
+static float collision_newton_rhapson(ParticleCollision *col, float radius, ParticleCollisionElement *pce, NRDistanceFunc distance_func)
+{
+ float t0, t1, dt_init, d0, d1, dd, n[3];
+ int iter;
+
+ pce->inv_nor = -1;
+
+ if (col->inv_total_time > 0.0f) {
+ /* Initial step size should be small, but not too small or floating point
+ * precision errors will appear. - z0r */
+ dt_init = COLLISION_INIT_STEP * col->inv_total_time;
+ }
+ else {
+ dt_init = 0.001f;
+ }
+
+ /* start from the beginning */
+ t0 = 0.f;
+ collision_interpolate_element(pce, t0, col->f, col);
+ d0 = distance_func(col->co1, radius, pce, n);
+ t1 = dt_init;
+ d1 = 0.f;
+
+ for (iter=0; iter<10; iter++) {//, itersum++) {
+ /* get current location */
+ collision_interpolate_element(pce, t1, col->f, col);
+ interp_v3_v3v3(pce->p, col->co1, col->co2, t1);
+
+ d1 = distance_func(pce->p, radius, pce, n);
+
+ /* particle already inside face, so report collision */
+ if (iter == 0 && d0 < 0.f && d0 > -radius) {
+ copy_v3_v3(pce->p, col->co1);
+ copy_v3_v3(pce->nor, n);
+ pce->inside = 1;
+ return 0.f;
+ }
+
+ /* Zero gradient (no movement relative to element). Can't step from
+ * here. */
+ if (d1 == d0) {
+ /* If first iteration, try from other end where the gradient may be
+ * greater. Note: code duplicated below. */
+ if (iter == 0) {
+ t0 = 1.f;
+ collision_interpolate_element(pce, t0, col->f, col);
+ d0 = distance_func(col->co2, radius, pce, n);
+ t1 = 1.0f - dt_init;
+ d1 = 0.f;
+ continue;
+ }
+ else
+ return -1.f;
+ }
+
+ dd = (t1-t0)/(d1-d0);
+
+ t0 = t1;
+ d0 = d1;
+
+ t1 -= d1*dd;
+
+ /* Particle moving away from plane could also mean a strangely rotating
+ * face, so check from end. Note: code duplicated above. */
+ if (iter == 0 && t1 < 0.f) {
+ t0 = 1.f;
+ collision_interpolate_element(pce, t0, col->f, col);
+ d0 = distance_func(col->co2, radius, pce, n);
+ t1 = 1.0f - dt_init;
+ d1 = 0.f;
+ continue;
+ }
+ else if (iter == 1 && (t1 < -COLLISION_ZERO || t1 > 1.f))
+ return -1.f;
+
+ if (d1 <= COLLISION_ZERO && d1 >= -COLLISION_ZERO) {
+ if (t1 >= -COLLISION_ZERO && t1 <= 1.f) {
+ if (distance_func == nr_signed_distance_to_plane)
+ copy_v3_v3(pce->nor, n);
+
+ CLAMP(t1, 0.f, 1.f);
+
+ return t1;
+ }
+ else
+ return -1.f;
+ }
+ }
+ return -1.0;
+}
+static int collision_sphere_to_tri(ParticleCollision *col, float radius, ParticleCollisionElement *pce, float *t)
+{
+ ParticleCollisionElement *result = &col->pce;
+ float ct, u, v;
+
+ pce->inv_nor = -1;
+ pce->inside = 0;
+
+ ct = collision_newton_rhapson(col, radius, pce, nr_signed_distance_to_plane);
+
+ if (ct >= 0.f && ct < *t && (result->inside==0 || pce->inside==1) ) {
+ float e1[3], e2[3], p0[3];
+ float e1e1, e1e2, e1p0, e2e2, e2p0, inv;
+
+ sub_v3_v3v3(e1, pce->x1, pce->x0);
+ sub_v3_v3v3(e2, pce->x2, pce->x0);
+ /* XXX: add radius correction here? */
+ sub_v3_v3v3(p0, pce->p, pce->x0);
+
+ e1e1 = dot_v3v3(e1, e1);
+ e1e2 = dot_v3v3(e1, e2);
+ e1p0 = dot_v3v3(e1, p0);
+ e2e2 = dot_v3v3(e2, e2);
+ e2p0 = dot_v3v3(e2, p0);
+
+ inv = 1.f/(e1e1 * e2e2 - e1e2 * e1e2);
+ u = (e2e2 * e1p0 - e1e2 * e2p0) * inv;
+ v = (e1e1 * e2p0 - e1e2 * e1p0) * inv;
+
+ if (u>=0.f && u<=1.f && v>=0.f && u+v<=1.f) {
+ *result = *pce;
+
+ /* normal already calculated in pce */
+
+ result->uv[0] = u;
+ result->uv[1] = v;
+
+ *t = ct;
+ return 1;
+ }
+ }
+ return 0;
+}
+static int collision_sphere_to_edges(ParticleCollision *col, float radius, ParticleCollisionElement *pce, float *t)
+{
+ ParticleCollisionElement edge[3], *cur = NULL, *hit = NULL;
+ ParticleCollisionElement *result = &col->pce;
+
+ float ct;
+ int i;
+
+ for (i=0; i<3; i++) {
+ cur = edge+i;
+ cur->x[0] = pce->x[i]; cur->x[1] = pce->x[(i+1)%3];
+ cur->v[0] = pce->v[i]; cur->v[1] = pce->v[(i+1)%3];
+ cur->tot = 2;
+ cur->inside = 0;
+
+ ct = collision_newton_rhapson(col, radius, cur, nr_distance_to_edge);
+
+ if (ct >= 0.f && ct < *t) {
+ float u, e[3], vec[3];
+
+ sub_v3_v3v3(e, cur->x1, cur->x0);
+ sub_v3_v3v3(vec, cur->p, cur->x0);
+ u = dot_v3v3(vec, e) / dot_v3v3(e, e);
+
+ if (u < 0.f || u > 1.f)
+ break;
+
+ *result = *cur;
+
+ madd_v3_v3v3fl(result->nor, vec, e, -u);
+ normalize_v3(result->nor);
+
+ result->uv[0] = u;
+
+
+ hit = cur;
+ *t = ct;
+ }
+
+ }
+
+ return hit != NULL;
+}
+static int collision_sphere_to_verts(ParticleCollision *col, float radius, ParticleCollisionElement *pce, float *t)
+{
+ ParticleCollisionElement vert[3], *cur = NULL, *hit = NULL;
+ ParticleCollisionElement *result = &col->pce;
+
+ float ct;
+ int i;
+
+ for (i=0; i<3; i++) {
+ cur = vert+i;
+ cur->x[0] = pce->x[i];
+ cur->v[0] = pce->v[i];
+ cur->tot = 1;
+ cur->inside = 0;
+
+ ct = collision_newton_rhapson(col, radius, cur, nr_distance_to_vert);
+
+ if (ct >= 0.f && ct < *t) {
+ *result = *cur;
+
+ sub_v3_v3v3(result->nor, cur->p, cur->x0);
+ normalize_v3(result->nor);
+
+ hit = cur;
+ *t = ct;
+ }
+
+ }
+
+ return hit != NULL;
+}
+/* Callback for BVHTree near test */
+void BKE_psys_collision_neartest_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
+{
+ ParticleCollision *col = (ParticleCollision *) userdata;
+ ParticleCollisionElement pce;
+ const MVertTri *vt = &col->md->tri[index];
+ MVert *x = col->md->x;
+ MVert *v = col->md->current_v;
+ float t = hit->dist/col->original_ray_length;
+ int collision = 0;
+
+ pce.x[0] = x[vt->tri[0]].co;
+ pce.x[1] = x[vt->tri[1]].co;
+ pce.x[2] = x[vt->tri[2]].co;
+
+ pce.v[0] = v[vt->tri[0]].co;
+ pce.v[1] = v[vt->tri[1]].co;
+ pce.v[2] = v[vt->tri[2]].co;
+
+ pce.tot = 3;
+ pce.inside = 0;
+ pce.index = index;
+
+ collision = collision_sphere_to_tri(col, ray->radius, &pce, &t);
+ if (col->pce.inside == 0) {
+ collision += collision_sphere_to_edges(col, ray->radius, &pce, &t);
+ collision += collision_sphere_to_verts(col, ray->radius, &pce, &t);
+ }
+
+ if (collision) {
+ hit->dist = col->original_ray_length * t;
+ hit->index = index;
+
+ collision_point_velocity(&col->pce);
+
+ col->hit = col->current;
+ }
+}
+static int collision_detect(ParticleData *pa, ParticleCollision *col, BVHTreeRayHit *hit, ListBase *colliders)
+{
+ const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT);
+ ColliderCache *coll;
+ float ray_dir[3];
+
+ if (BLI_listbase_is_empty(colliders))
+ return 0;
+
+ sub_v3_v3v3(ray_dir, col->co2, col->co1);
+ hit->index = -1;
+ hit->dist = col->original_ray_length = normalize_v3(ray_dir);
+ col->pce.inside = 0;
+
+ /* even if particle is stationary we want to check for moving colliders */
+ /* if hit.dist is zero the bvhtree_ray_cast will just ignore everything */
+ if (hit->dist == 0.0f)
+ hit->dist = col->original_ray_length = 0.000001f;
+
+ for (coll = colliders->first; coll; coll=coll->next) {
+ /* for boids: don't check with current ground object; also skip if permeated */
+ bool skip = false;
+
+ for (int i = 0; i < col->skip_count; i++) {
+ if (coll->ob == col->skip[i]) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip)
+ continue;
+
+ /* particles should not collide with emitter at birth */
+ if (coll->ob == col->emitter && pa->time < col->cfra && pa->time >= col->old_cfra)
+ continue;
+
+ col->current = coll->ob;
+ col->md = coll->collmd;
+ col->fac1 = (col->old_cfra - coll->collmd->time_x) / (coll->collmd->time_xnew - coll->collmd->time_x);
+ col->fac2 = (col->cfra - coll->collmd->time_x) / (coll->collmd->time_xnew - coll->collmd->time_x);
+
+ if (col->md && col->md->bvhtree) {
+ BLI_bvhtree_ray_cast_ex(
+ col->md->bvhtree, col->co1, ray_dir, col->radius, hit,
+ BKE_psys_collision_neartest_cb, col, raycast_flag);
+ }
+ }
+
+ return hit->index >= 0;
+}
+static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeRayHit *hit, int kill, int dynamic_rotation)
+{
+ ParticleCollisionElement *pce = &col->pce;
+ PartDeflect *pd = col->hit->pd;
+ float co[3]; /* point of collision */
+ float x = hit->dist/col->original_ray_length; /* location factor of collision between this iteration */
+ float f = col->f + x * (1.0f - col->f); /* time factor of collision between timestep */
+ float dt1 = (f - col->f) * col->total_time; /* time since previous collision (in seconds) */
+ float dt2 = (1.0f - f) * col->total_time; /* time left after collision (in seconds) */
+ int through = (BLI_frand() < pd->pdef_perm) ? 1 : 0; /* did particle pass through the collision surface? */
+
+ /* calculate exact collision location */
+ interp_v3_v3v3(co, col->co1, col->co2, x);
+
+ /* particle dies in collision */
+ if (through == 0 && (kill || pd->flag & PDEFLE_KILL_PART)) {
+ pa->alive = PARS_DYING;
+ pa->dietime = col->old_cfra + (col->cfra - col->old_cfra) * f;
+
+ copy_v3_v3(pa->state.co, co);
+ interp_v3_v3v3(pa->state.vel, pa->prev_state.vel, pa->state.vel, f);
+ interp_qt_qtqt(pa->state.rot, pa->prev_state.rot, pa->state.rot, f);
+ interp_v3_v3v3(pa->state.ave, pa->prev_state.ave, pa->state.ave, f);
+
+ /* particle is dead so we don't need to calculate further */
+ return 0;
+ }
+ /* figure out velocity and other data after collision */
+ else {
+ float v0[3]; /* velocity directly before collision to be modified into velocity directly after collision */
+ float v0_nor[3];/* normal component of v0 */
+ float v0_tan[3];/* tangential component of v0 */
+ float vc_tan[3];/* tangential component of collision surface velocity */
+ float v0_dot, vc_dot;
+ float damp = pd->pdef_damp + pd->pdef_rdamp * 2 * (BLI_frand() - 0.5f);
+ float frict = pd->pdef_frict + pd->pdef_rfrict * 2 * (BLI_frand() - 0.5f);
+ float distance, nor[3], dot;
+
+ CLAMP(damp,0.0f, 1.0f);
+ CLAMP(frict,0.0f, 1.0f);
+
+ /* get exact velocity right before collision */
+ madd_v3_v3v3fl(v0, col->ve1, col->acc, dt1);
+
+ /* convert collider velocity from 1/framestep to 1/s TODO: here we assume 1 frame step for collision modifier */
+ mul_v3_fl(pce->vel, col->inv_timestep);
+
+ /* calculate tangential particle velocity */
+ v0_dot = dot_v3v3(pce->nor, v0);
+ madd_v3_v3v3fl(v0_tan, v0, pce->nor, -v0_dot);
+
+ /* calculate tangential collider velocity */
+ vc_dot = dot_v3v3(pce->nor, pce->vel);
+ madd_v3_v3v3fl(vc_tan, pce->vel, pce->nor, -vc_dot);
+
+ /* handle friction effects (tangential and angular velocity) */
+ if (frict > 0.0f) {
+ /* angular <-> linear velocity */
+ if (dynamic_rotation) {
+ float vr_tan[3], v1_tan[3], ave[3];
+
+ /* linear velocity of particle surface */
+ cross_v3_v3v3(vr_tan, pce->nor, pa->state.ave);
+ mul_v3_fl(vr_tan, pa->size);
+
+ /* change to coordinates that move with the collision plane */
+ sub_v3_v3v3(v1_tan, v0_tan, vc_tan);
+
+ /* The resulting velocity is a weighted average of particle cm & surface
+ * velocity. This weight (related to particle's moment of inertia) could
+ * be made a parameter for angular <-> linear conversion.
+ */
+ madd_v3_v3fl(v1_tan, vr_tan, -0.4);
+ mul_v3_fl(v1_tan, 1.0f/1.4f); /* 1/(1+0.4) */
+
+ /* rolling friction is around 0.01 of sliding friction (could be made a parameter) */
+ mul_v3_fl(v1_tan, 1.0f - 0.01f * frict);
+
+ /* surface_velocity is opposite to cm velocity */
+ negate_v3_v3(vr_tan, v1_tan);
+
+ /* get back to global coordinates */
+ add_v3_v3(v1_tan, vc_tan);
+
+ /* convert to angular velocity*/
+ cross_v3_v3v3(ave, vr_tan, pce->nor);
+ mul_v3_fl(ave, 1.0f/MAX2(pa->size, 0.001f));
+
+ /* only friction will cause change in linear & angular velocity */
+ interp_v3_v3v3(pa->state.ave, pa->state.ave, ave, frict);
+ interp_v3_v3v3(v0_tan, v0_tan, v1_tan, frict);
+ }
+ else {
+ /* just basic friction (unphysical due to the friction model used in Blender) */
+ interp_v3_v3v3(v0_tan, v0_tan, vc_tan, frict);
+ }
+ }
+
+ /* stickiness was possibly added before, so cancel that before calculating new normal velocity */
+ /* otherwise particles go flying out of the surface because of high reversed sticky velocity */
+ if (v0_dot < 0.0f) {
+ v0_dot += pd->pdef_stickness;
+ if (v0_dot > 0.0f)
+ v0_dot = 0.0f;
+ }
+
+ /* damping and flipping of velocity around normal */
+ v0_dot *= 1.0f - damp;
+ vc_dot *= through ? damp : 1.0f;
+
+ /* calculate normal particle velocity */
+ /* special case for object hitting the particle from behind */
+ if (through==0 && ((vc_dot>0.0f && v0_dot>0.0f && vc_dot>v0_dot) || (vc_dot<0.0f && v0_dot<0.0f && vc_dot<v0_dot)))
+ mul_v3_v3fl(v0_nor, pce->nor, vc_dot);
+ else if (v0_dot > 0.f)
+ mul_v3_v3fl(v0_nor, pce->nor, vc_dot + v0_dot);
+ else
+ mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? 1.0f : -1.0f) * v0_dot);
+
+ /* combine components together again */
+ add_v3_v3v3(v0, v0_nor, v0_tan);
+
+ if (col->boid) {
+ /* keep boids above ground */
+ BoidParticle *bpa = pa->boid;
+ if (bpa->data.mode == eBoidMode_OnLand || co[2] <= col->boid_z) {
+ co[2] = col->boid_z;
+ v0[2] = 0.0f;
+ }
+ }
+
+ /* re-apply acceleration to final location and velocity */
+ madd_v3_v3v3fl(pa->state.co, co, v0, dt2);
+ madd_v3_v3fl(pa->state.co, col->acc, 0.5f*dt2*dt2);
+ madd_v3_v3v3fl(pa->state.vel, v0, col->acc, dt2);
+
+ /* make sure particle stays on the right side of the surface */
+ if (!through) {
+ distance = collision_point_distance_with_normal(co, pce, -1.f, col, nor);
+
+ if (distance < col->radius + COLLISION_MIN_DISTANCE)
+ madd_v3_v3fl(co, nor, col->radius + COLLISION_MIN_DISTANCE - distance);
+
+ dot = dot_v3v3(nor, v0);
+ if (dot < 0.f)
+ madd_v3_v3fl(v0, nor, -dot);
+
+ distance = collision_point_distance_with_normal(pa->state.co, pce, 1.f, col, nor);
+
+ if (distance < col->radius + COLLISION_MIN_DISTANCE)
+ madd_v3_v3fl(pa->state.co, nor, col->radius + COLLISION_MIN_DISTANCE - distance);
+
+ dot = dot_v3v3(nor, pa->state.vel);
+ if (dot < 0.f)
+ madd_v3_v3fl(pa->state.vel, nor, -dot);
+ }
+
+ /* add stickiness to surface */
+ madd_v3_v3fl(pa->state.vel, pce->nor, -pd->pdef_stickness);
+
+ /* set coordinates for next iteration */
+ copy_v3_v3(col->co1, co);
+ copy_v3_v3(col->co2, pa->state.co);
+
+ copy_v3_v3(col->ve1, v0);
+ copy_v3_v3(col->ve2, pa->state.vel);
+
+ col->f = f;
+ }
+
+ /* if permeability random roll succeeded, disable collider for this sim step */
+ if (through) {
+ col->skip[col->skip_count++] = col->hit;
+ }
+
+ return 1;
+}
+static void collision_fail(ParticleData *pa, ParticleCollision *col)
+{
+ /* final chance to prevent total failure, so stick to the surface and hope for the best */
+ collision_point_on_surface(col->co1, &col->pce, 1.f, col, pa->state.co);
+
+ copy_v3_v3(pa->state.vel, col->pce.vel);
+ mul_v3_fl(pa->state.vel, col->inv_timestep);
+
+
+ /* printf("max iterations\n"); */
+}
+
+/* Particle - Mesh collision detection and response
+ * Features:
+ * -friction and damping
+ * -angular momentum <-> linear momentum
+ * -high accuracy by re-applying particle acceleration after collision
+ * -handles moving, rotating and deforming meshes
+ * -uses Newton-Rhapson iteration to find the collisions
+ * -handles spherical particles and (nearly) point like particles
+ */
+static void collision_check(ParticleSimulationData *sim, int p, float dfra, float cfra)
+{
+ ParticleSettings *part = sim->psys->part;
+ ParticleData *pa = sim->psys->particles + p;
+ ParticleCollision col;
+ BVHTreeRayHit hit;
+ int collision_count=0;
+
+ float timestep = psys_get_timestep(sim);
+
+ memset(&col, 0, sizeof(ParticleCollision));
+
+ col.total_time = timestep * dfra;
+ col.inv_total_time = 1.0f/col.total_time;
+ col.inv_timestep = 1.0f/timestep;
+
+ col.cfra = cfra;
+ col.old_cfra = sim->psys->cfra;
+
+ /* get acceleration (from gravity, forcefields etc. to be re-applied in collision response) */
+ sub_v3_v3v3(col.acc, pa->state.vel, pa->prev_state.vel);
+ mul_v3_fl(col.acc, 1.f/col.total_time);
+
+ /* set values for first iteration */
+ copy_v3_v3(col.co1, pa->prev_state.co);
+ copy_v3_v3(col.co2, pa->state.co);
+ copy_v3_v3(col.ve1, pa->prev_state.vel);
+ copy_v3_v3(col.ve2, pa->state.vel);
+ col.f = 0.0f;
+
+ col.radius = ((part->flag & PART_SIZE_DEFL) || (part->phystype == PART_PHYS_BOIDS)) ? pa->size : COLLISION_MIN_RADIUS;
+
+ /* override for boids */
+ if (part->phystype == PART_PHYS_BOIDS && part->boids->options & BOID_ALLOW_LAND) {
+ col.boid = 1;
+ col.boid_z = pa->state.co[2];
+ col.skip[col.skip_count++] = pa->boid->ground;
+ }
+
+ /* 10 iterations to catch multiple collisions */
+ while (collision_count < PARTICLE_COLLISION_MAX_COLLISIONS) {
+ if (collision_detect(pa, &col, &hit, sim->colliders)) {
+
+ collision_count++;
+
+ if (collision_count == PARTICLE_COLLISION_MAX_COLLISIONS)
+ collision_fail(pa, &col);
+ else if (collision_response(pa, &col, &hit, part->flag & PART_DIE_ON_COL, part->flag & PART_ROT_DYN)==0)
+ return;
+ }
+ else
+ return;
+ }
+}
+/************************************************/
+/* Hair */
+/************************************************/
+/* check if path cache or children need updating and do it if needed */
+static void psys_update_path_cache(ParticleSimulationData *sim, float cfra, const bool use_render_params)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleEditSettings *pset = &sim->scene->toolsettings->particle;
+ Base *base;
+ int distr=0, alloc=0, skip=0;
+
+ if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET)
+ alloc=1;
+
+ if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT)))
+ distr=1;
+
+ if (distr) {
+ if (alloc)
+ realloc_particles(sim, sim->psys->totpart);
+
+ if (psys_get_tot_child(sim->scene, psys)) {
+ /* don't generate children while computing the hair keys */
+ if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) {
+ distribute_particles(sim, PART_FROM_CHILD);
+
+ if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f)
+ psys_find_parents(sim, use_render_params);
+ }
+ }
+ else
+ psys_free_children(psys);
+ }
+
+ if ((part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED)==0)
+ skip = 1; /* only hair, keyed and baked stuff can have paths */
+ else if (part->ren_as != PART_DRAW_PATH && !(part->type==PART_HAIR && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)))
+ skip = 1; /* particle visualization must be set as path */
+ else if (!psys->renderdata) {
+ if (part->draw_as != PART_DRAW_REND)
+ skip = 1; /* draw visualization */
+ else if (psys->pointcache->flag & PTCACHE_BAKING)
+ skip = 1; /* no need to cache paths while baking dynamics */
+ else if (psys_in_edit_mode(sim->scene, psys)) {
+ if ((pset->flag & PE_DRAW_PART)==0)
+ skip = 1;
+ else if (part->childtype==0 && (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED)==0)
+ skip = 1; /* in edit mode paths are needed for child particles and dynamic hair */
+ }
+ }
+
+
+ /* particle instance modifier with "path" option need cached paths even if particle system doesn't */
+ for (base = sim->scene->base.first; base; base= base->next) {
+ ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance);
+ if (md) {
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md;
+ if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) {
+ skip = 0;
+ break;
+ }
+ }
+ }
+
+ if (!skip) {
+ psys_cache_paths(sim, cfra, use_render_params);
+
+ /* for render, child particle paths are computed on the fly */
+ if (part->childtype) {
+ if (!psys->totchild)
+ skip = 1;
+ else if (psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DONE)==0)
+ skip = 1;
+
+ if (!skip)
+ psys_cache_child_paths(sim, cfra, 0, use_render_params);
+ }
+ }
+ else if (psys->pathcache)
+ psys_free_path_cache(psys, NULL);
+}
+
+static bool psys_hair_use_simulation(ParticleData *pa, float max_length)
+{
+ /* Minimum segment length relative to average length.
+ * Hairs with segments below this length will be excluded from the simulation,
+ * because otherwise the solver will become unstable.
+ * The hair system should always make sure the hair segments have reasonable length ratios,
+ * but this can happen in old files when e.g. cutting hair.
+ */
+ const float min_length = 0.1f * max_length;
+
+ HairKey *key;
+ int k;
+
+ if (pa->totkey < 2)
+ return false;
+
+ for (k=1, key=pa->hair+1; k<pa->totkey; k++,key++) {
+ float length = len_v3v3(key->co, (key-1)->co);
+ if (length < min_length)
+ return false;
+ }
+
+ return true;
+}
+
+static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight)
+{
+ if (dvert) {
+ if (!dvert->totweight) {
+ dvert->dw = MEM_callocN(sizeof(MDeformWeight), "deformWeight");
+ dvert->totweight = 1;
+ }
+
+ dvert->dw->weight = weight;
+ dvert++;
+ }
+ return dvert;
+}
+
+static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int totedge, DerivedMesh **r_dm, ClothHairData **r_hairdata)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ DerivedMesh *dm;
+ ClothHairData *hairdata;
+ MVert *mvert;
+ MEdge *medge;
+ MDeformVert *dvert;
+ HairKey *key;
+ PARTICLE_P;
+ int k, hair_index;
+ float hairmat[4][4];
+ float max_length;
+ float hair_radius;
+
+ dm = *r_dm;
+ if (!dm) {
+ *r_dm = dm = CDDM_new(totpoint, totedge, 0, 0, 0);
+ DM_add_vert_layer(dm, CD_MDEFORMVERT, CD_CALLOC, NULL);
+ }
+ mvert = CDDM_get_verts(dm);
+ medge = CDDM_get_edges(dm);
+ dvert = DM_get_vert_data_layer(dm, CD_MDEFORMVERT);
+
+ hairdata = *r_hairdata;
+ if (!hairdata) {
+ *r_hairdata = hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data");
+ }
+
+ /* calculate maximum segment length */
+ max_length = 0.0f;
+ LOOP_PARTICLES {
+ for (k=1, key=pa->hair+1; k<pa->totkey; k++,key++) {
+ float length = len_v3v3(key->co, (key-1)->co);
+ if (max_length < length)
+ max_length = length;
+ }
+ }
+
+ psys->clmd->sim_parms->vgroup_mass = 1;
+
+ /* XXX placeholder for more flexible future hair settings */
+ hair_radius = part->size;
+
+ /* make vgroup for pin roots etc.. */
+ hair_index = 1;
+ LOOP_PARTICLES {
+ float root_mat[4][4];
+ float bending_stiffness;
+ bool use_hair;
+
+ pa->hair_index = hair_index;
+ use_hair = psys_hair_use_simulation(pa, max_length);
+
+ psys_mat_hair_to_object(sim->ob, sim->psmd->dm_final, psys->part->from, pa, hairmat);
+ mul_m4_m4m4(root_mat, sim->ob->obmat, hairmat);
+ normalize_m4(root_mat);
+
+ bending_stiffness = CLAMPIS(1.0f - part->bending_random * psys_frand(psys, p + 666), 0.0f, 1.0f);
+
+ for (k=0, key=pa->hair; k<pa->totkey; k++,key++) {
+ ClothHairData *hair;
+ float *co, *co_next;
+
+ co = key->co;
+ co_next = (key+1)->co;
+
+ /* create fake root before actual root to resist bending */
+ if (k==0) {
+ hair = &psys->clmd->hairdata[pa->hair_index - 1];
+ copy_v3_v3(hair->loc, root_mat[3]);
+ copy_m3_m4(hair->rot, root_mat);
+
+ hair->radius = hair_radius;
+ hair->bending_stiffness = bending_stiffness;
+
+ add_v3_v3v3(mvert->co, co, co);
+ sub_v3_v3(mvert->co, co_next);
+ mul_m4_v3(hairmat, mvert->co);
+
+ medge->v1 = pa->hair_index - 1;
+ medge->v2 = pa->hair_index;
+
+ dvert = hair_set_pinning(dvert, 1.0f);
+
+ mvert++;
+ medge++;
+ }
+
+ /* store root transform in cloth data */
+ hair = &psys->clmd->hairdata[pa->hair_index + k];
+ copy_v3_v3(hair->loc, root_mat[3]);
+ copy_m3_m4(hair->rot, root_mat);
+
+ hair->radius = hair_radius;
+ hair->bending_stiffness = bending_stiffness;
+
+ copy_v3_v3(mvert->co, co);
+ mul_m4_v3(hairmat, mvert->co);
+
+ if (k) {
+ medge->v1 = pa->hair_index + k - 1;
+ medge->v2 = pa->hair_index + k;
+ }
+
+ /* roots and disabled hairs should be 1.0, the rest can be anything from 0.0 to 1.0 */
+ if (use_hair)
+ dvert = hair_set_pinning(dvert, key->weight);
+ else
+ dvert = hair_set_pinning(dvert, 1.0f);
+
+ mvert++;
+ if (k)
+ medge++;
+ }
+
+ hair_index += pa->totkey + 1;
+ }
+}
+
+static void do_hair_dynamics(ParticleSimulationData *sim)
+{
+ ParticleSystem *psys = sim->psys;
+ PARTICLE_P;
+ EffectorWeights *clmd_effweights;
+ int totpoint;
+ int totedge;
+ float (*deformedVerts)[3];
+ bool realloc_roots;
+
+ if (!psys->clmd) {
+ psys->clmd = (ClothModifierData*)modifier_new(eModifierType_Cloth);
+ psys->clmd->sim_parms->goalspring = 0.0f;
+ psys->clmd->sim_parms->vel_damping = 1.0f;
+ psys->clmd->sim_parms->flags |= CLOTH_SIMSETTINGS_FLAG_GOAL|CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
+ psys->clmd->coll_parms->flags &= ~CLOTH_COLLSETTINGS_FLAG_SELF;
+ }
+
+ /* count simulated points */
+ totpoint = 0;
+ totedge = 0;
+ LOOP_PARTICLES {
+ /* "out" dm contains all hairs */
+ totedge += pa->totkey;
+ totpoint += pa->totkey + 1; /* +1 for virtual root point */
+ }
+
+ realloc_roots = false; /* whether hair root info array has to be reallocated */
+ if (psys->hair_in_dm) {
+ DerivedMesh *dm = psys->hair_in_dm;
+ if (totpoint != dm->getNumVerts(dm) || totedge != dm->getNumEdges(dm)) {
+ dm->release(dm);
+ psys->hair_in_dm = NULL;
+ realloc_roots = true;
+ }
+ }
+
+ if (!psys->hair_in_dm || !psys->clmd->hairdata || realloc_roots) {
+ if (psys->clmd->hairdata) {
+ MEM_freeN(psys->clmd->hairdata);
+ psys->clmd->hairdata = NULL;
+ }
+ }
+
+ hair_create_input_dm(sim, totpoint, totedge, &psys->hair_in_dm, &psys->clmd->hairdata);
+
+ if (psys->hair_out_dm)
+ psys->hair_out_dm->release(psys->hair_out_dm);
+
+ psys->clmd->point_cache = psys->pointcache;
+ /* for hair sim we replace the internal cloth effector weights temporarily
+ * to use the particle settings
+ */
+ clmd_effweights = psys->clmd->sim_parms->effector_weights;
+ psys->clmd->sim_parms->effector_weights = psys->part->effector_weights;
+
+ deformedVerts = MEM_mallocN(sizeof(*deformedVerts) * psys->hair_in_dm->getNumVerts(psys->hair_in_dm), "do_hair_dynamics vertexCos");
+ psys->hair_out_dm = CDDM_copy(psys->hair_in_dm);
+ psys->hair_out_dm->getVertCos(psys->hair_out_dm, deformedVerts);
+
+ clothModifier_do(psys->clmd, sim->scene, sim->ob, psys->hair_in_dm, deformedVerts);
+
+ CDDM_apply_vert_coords(psys->hair_out_dm, deformedVerts);
+
+ MEM_freeN(deformedVerts);
+
+ /* restore cloth effector weights */
+ psys->clmd->sim_parms->effector_weights = clmd_effweights;
+}
+static void hair_step(ParticleSimulationData *sim, float cfra, const bool use_render_params)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ PARTICLE_P;
+ float disp = psys_get_current_display_percentage(psys);
+
+ LOOP_PARTICLES {
+ pa->size = part->size;
+ if (part->randsize > 0.0f)
+ pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
+
+ if (psys_frand(psys, p) > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+
+ if (psys->recalc & PSYS_RECALC_RESET) {
+ /* need this for changing subsurf levels */
+ psys_calc_dmcache(sim->ob, sim->psmd->dm_final, sim->psmd->dm_deformed, psys);
+
+ if (psys->clmd)
+ cloth_free_modifier(psys->clmd);
+ }
+
+ /* dynamics with cloth simulation, psys->particles can be NULL with 0 particles [#25519] */
+ if (psys->part->type==PART_HAIR && psys->flag & PSYS_HAIR_DYNAMICS && psys->particles)
+ do_hair_dynamics(sim);
+
+ /* following lines were removed r29079 but cause bug [#22811], see report for details */
+ psys_update_effectors(sim);
+ psys_update_path_cache(sim, cfra, use_render_params);
+
+ psys->flag |= PSYS_HAIR_UPDATED;
+}
+
+static void save_hair(ParticleSimulationData *sim, float UNUSED(cfra))
+{
+ Object *ob = sim->ob;
+ ParticleSystem *psys = sim->psys;
+ HairKey *key, *root;
+ PARTICLE_P;
+
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ psys->lattice_deform_data= psys_create_lattice_deform_data(sim);
+
+ if (psys->totpart==0) return;
+
+ /* save new keys for elements if needed */
+ LOOP_PARTICLES {
+ /* first time alloc */
+ if (pa->totkey==0 || pa->hair==NULL) {
+ pa->hair = MEM_callocN((psys->part->hair_step + 1) * sizeof(HairKey), "HairKeys");
+ pa->totkey = 0;
+ }
+
+ key = root = pa->hair;
+ key += pa->totkey;
+
+ /* convert from global to geometry space */
+ copy_v3_v3(key->co, pa->state.co);
+ mul_m4_v3(ob->imat, key->co);
+
+ if (pa->totkey) {
+ sub_v3_v3(key->co, root->co);
+ psys_vec_rot_to_face(sim->psmd->dm_final, pa, key->co);
+ }
+
+ key->time = pa->state.time;
+
+ key->weight = 1.0f - key->time / 100.0f;
+
+ pa->totkey++;
+
+ /* root is always in the origin of hair space so we set it to be so after the last key is saved*/
+ if (pa->totkey == psys->part->hair_step + 1) {
+ zero_v3(root->co);
+ }
+
+ }
+}
+
+/* Code for an adaptive time step based on the Courant-Friedrichs-Lewy
+ * condition. */
+static const float MIN_TIMESTEP = 1.0f / 101.0f;
+/* Tolerance of 1.5 means the last subframe neither favors growing nor
+ * shrinking (e.g if it were 1.3, the last subframe would tend to be too
+ * small). */
+static const float TIMESTEP_EXPANSION_FACTOR = 0.1f;
+static const float TIMESTEP_EXPANSION_TOLERANCE = 1.5f;
+
+/* Calculate the speed of the particle relative to the local scale of the
+ * simulation. This should be called once per particle during a simulation
+ * step, after the velocity has been updated. element_size defines the scale of
+ * the simulation, and is typically the distance to neighboring particles. */
+static void update_courant_num(ParticleSimulationData *sim, ParticleData *pa,
+ float dtime, SPHData *sphdata, SpinLock *spin)
+{
+ float relative_vel[3];
+
+ sub_v3_v3v3(relative_vel, pa->prev_state.vel, sphdata->flow);
+
+ const float courant_num = len_v3(relative_vel) * dtime / sphdata->element_size;
+ if (sim->courant_num < courant_num) {
+ BLI_spin_lock(spin);
+ if (sim->courant_num < courant_num) {
+ sim->courant_num = courant_num;
+ }
+ BLI_spin_unlock(spin);
+ }
+}
+static float get_base_time_step(ParticleSettings *part)
+{
+ return 1.0f / (float) (part->subframes + 1);
+}
+/* Update time step size to suit current conditions. */
+static void update_timestep(ParticleSystem *psys, ParticleSimulationData *sim)
+{
+ float dt_target;
+ if (sim->courant_num == 0.0f)
+ dt_target = 1.0f;
+ else
+ dt_target = psys->dt_frac * (psys->part->courant_target / sim->courant_num);
+
+ /* Make sure the time step is reasonable. For some reason, the CLAMP macro
+ * doesn't work here. The time step becomes too large. - z0r */
+ if (dt_target < MIN_TIMESTEP)
+ dt_target = MIN_TIMESTEP;
+ else if (dt_target > get_base_time_step(psys->part))
+ dt_target = get_base_time_step(psys->part);
+
+ /* Decrease time step instantly, but increase slowly. */
+ if (dt_target > psys->dt_frac)
+ psys->dt_frac = interpf(dt_target, psys->dt_frac, TIMESTEP_EXPANSION_FACTOR);
+ else
+ psys->dt_frac = dt_target;
+}
+
+static float sync_timestep(ParticleSystem *psys, float t_frac)
+{
+ /* Sync with frame end if it's close. */
+ if (t_frac == 1.0f)
+ return psys->dt_frac;
+ else if (t_frac + (psys->dt_frac * TIMESTEP_EXPANSION_TOLERANCE) >= 1.0f)
+ return 1.0f - t_frac;
+ else
+ return psys->dt_frac;
+}
+
+/************************************************/
+/* System Core */
+/************************************************/
+
+typedef struct DynamicStepSolverTaskData {
+ ParticleSimulationData *sim;
+
+ float cfra;
+ float timestep;
+ float dtime;
+
+ SpinLock spin;
+} DynamicStepSolverTaskData;
+
+static void dynamics_step_sph_ddr_task_cb_ex(
+ void *userdata, void *userdata_chunk, const int p, const int UNUSED(thread_id))
+{
+ DynamicStepSolverTaskData *data = userdata;
+ ParticleSimulationData *sim = data->sim;
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+
+ SPHData *sphdata = userdata_chunk;
+
+ ParticleData *pa;
+
+ if ((pa = psys->particles + p)->state.time <= 0.0f) {
+ return;
+ }
+
+ /* do global forces & effectors */
+ basic_integrate(sim, p, pa->state.time, data->cfra);
+
+ /* actual fluids calculations */
+ sph_integrate(sim, pa, pa->state.time, sphdata);
+
+ if (sim->colliders)
+ collision_check(sim, p, pa->state.time, data->cfra);
+
+ /* SPH particles are not physical particles, just interpolation
+ * particles, thus rotation has not a direct sense for them */
+ basic_rotate(part, pa, pa->state.time, data->timestep);
+
+ if (part->time_flag & PART_TIME_AUTOSF) {
+ update_courant_num(sim, pa, data->dtime, sphdata, &data->spin);
+ }
+}
+
+static void dynamics_step_sph_classical_basic_integrate_task_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int p, const int UNUSED(thread_id))
+{
+ DynamicStepSolverTaskData *data = userdata;
+ ParticleSimulationData *sim = data->sim;
+ ParticleSystem *psys = sim->psys;
+
+ ParticleData *pa;
+
+ if ((pa = psys->particles + p)->state.time <= 0.0f) {
+ return;
+ }
+
+ basic_integrate(sim, p, pa->state.time, data->cfra);
+}
+
+static void dynamics_step_sph_classical_calc_density_task_cb_ex(
+ void *userdata, void *userdata_chunk, const int p, const int UNUSED(thread_id))
+{
+ DynamicStepSolverTaskData *data = userdata;
+ ParticleSimulationData *sim = data->sim;
+ ParticleSystem *psys = sim->psys;
+
+ SPHData *sphdata = userdata_chunk;
+
+ ParticleData *pa;
+
+ if ((pa = psys->particles + p)->state.time <= 0.0f) {
+ return;
+ }
+
+ sphclassical_calc_dens(pa, pa->state.time, sphdata);
+}
+
+static void dynamics_step_sph_classical_integrate_task_cb_ex(
+ void *userdata, void *userdata_chunk, const int p, const int UNUSED(thread_id))
+{
+ DynamicStepSolverTaskData *data = userdata;
+ ParticleSimulationData *sim = data->sim;
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+
+ SPHData *sphdata = userdata_chunk;
+
+ ParticleData *pa;
+
+ if ((pa = psys->particles + p)->state.time <= 0.0f) {
+ return;
+ }
+
+ /* actual fluids calculations */
+ sph_integrate(sim, pa, pa->state.time, sphdata);
+
+ if (sim->colliders)
+ collision_check(sim, p, pa->state.time, data->cfra);
+
+ /* SPH particles are not physical particles, just interpolation
+ * particles, thus rotation has not a direct sense for them */
+ basic_rotate(part, pa, pa->state.time, data->timestep);
+
+ if (part->time_flag & PART_TIME_AUTOSF) {
+ update_courant_num(sim, pa, data->dtime, sphdata, &data->spin);
+ }
+}
+
+/* unbaked particles are calculated dynamically */
+static void dynamics_step(ParticleSimulationData *sim, float cfra)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part=psys->part;
+ RNG *rng;
+ BoidBrainData bbd;
+ ParticleTexture ptex;
+ PARTICLE_P;
+ float timestep;
+ /* frame & time changes */
+ float dfra, dtime;
+ float birthtime, dietime;
+
+ /* where have we gone in time since last time */
+ dfra= cfra - psys->cfra;
+
+ timestep = psys_get_timestep(sim);
+ dtime= dfra*timestep;
+
+ if (dfra < 0.0f) {
+ LOOP_EXISTING_PARTICLES {
+ psys_get_texture(sim, pa, &ptex, PAMAP_SIZE, cfra);
+ pa->size = part->size*ptex.size;
+ if (part->randsize > 0.0f)
+ pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
+
+ reset_particle(sim, pa, dtime, cfra);
+ }
+ return;
+ }
+
+ BLI_srandom(31415926 + (int)cfra + psys->seed);
+ /* for now do both, boids us 'rng' */
+ rng = BLI_rng_new_srandom(31415926 + (int)cfra + psys->seed);
+
+ psys_update_effectors(sim);
+
+ if (part->type != PART_HAIR)
+ sim->colliders = get_collider_cache(sim->scene, sim->ob, part->collision_group);
+
+ /* initialize physics type specific stuff */
+ switch (part->phystype) {
+ case PART_PHYS_BOIDS:
+ {
+ ParticleTarget *pt = psys->targets.first;
+ bbd.sim = sim;
+ bbd.part = part;
+ bbd.cfra = cfra;
+ bbd.dfra = dfra;
+ bbd.timestep = timestep;
+ bbd.rng = rng;
+
+ psys_update_particle_tree(psys, cfra);
+
+ boids_precalc_rules(part, cfra);
+
+ for (; pt; pt=pt->next) {
+ ParticleSystem *psys_target = psys_get_target_system(sim->ob, pt);
+ if (psys_target && psys_target != psys) {
+ psys_update_particle_tree(psys_target, cfra);
+ }
+ }
+ break;
+ }
+ case PART_PHYS_FLUID:
+ {
+ ParticleTarget *pt = psys->targets.first;
+ psys_update_particle_bvhtree(psys, cfra);
+
+ for (; pt; pt=pt->next) { /* Updating others systems particle tree for fluid-fluid interaction */
+ if (pt->ob)
+ psys_update_particle_bvhtree(BLI_findlink(&pt->ob->particlesystem, pt->psys-1), cfra);
+ }
+ break;
+ }
+ }
+ /* initialize all particles for dynamics */
+ LOOP_SHOWN_PARTICLES {
+ copy_particle_key(&pa->prev_state,&pa->state,1);
+
+ psys_get_texture(sim, pa, &ptex, PAMAP_SIZE, cfra);
+
+ pa->size = part->size*ptex.size;
+ if (part->randsize > 0.0f)
+ pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
+
+ birthtime = pa->time;
+ dietime = pa->dietime;
+
+ /* store this, so we can do multiple loops over particles */
+ pa->state.time = dfra;
+
+ if (dietime <= cfra && psys->cfra < dietime) {
+ /* particle dies some time between this and last step */
+ pa->state.time = dietime - ((birthtime > psys->cfra) ? birthtime : psys->cfra);
+ pa->alive = PARS_DYING;
+ }
+ else if (birthtime <= cfra && birthtime >= psys->cfra) {
+ /* particle is born some time between this and last step*/
+ reset_particle(sim, pa, dfra*timestep, cfra);
+ pa->alive = PARS_ALIVE;
+ pa->state.time = cfra - birthtime;
+ }
+ else if (dietime < cfra) {
+ /* nothing to be done when particle is dead */
+ }
+
+ /* only reset unborn particles if they're shown or if the particle is born soon*/
+ if (pa->alive==PARS_UNBORN && (part->flag & PART_UNBORN || (cfra + psys->pointcache->step > pa->time))) {
+ reset_particle(sim, pa, dtime, cfra);
+ }
+ else if (part->phystype == PART_PHYS_NO) {
+ reset_particle(sim, pa, dtime, cfra);
+ }
+
+ if (ELEM(pa->alive, PARS_ALIVE, PARS_DYING)==0 || (pa->flag & (PARS_UNEXIST|PARS_NO_DISP)))
+ pa->state.time = -1.f;
+ }
+
+ switch (part->phystype) {
+ case PART_PHYS_NEWTON:
+ {
+ LOOP_DYNAMIC_PARTICLES {
+ /* do global forces & effectors */
+ basic_integrate(sim, p, pa->state.time, cfra);
+
+ /* deflection */
+ if (sim->colliders)
+ collision_check(sim, p, pa->state.time, cfra);
+
+ /* rotations */
+ basic_rotate(part, pa, pa->state.time, timestep);
+ }
+ break;
+ }
+ case PART_PHYS_BOIDS:
+ {
+ LOOP_DYNAMIC_PARTICLES {
+ bbd.goal_ob = NULL;
+
+ boid_brain(&bbd, p, pa);
+
+ if (pa->alive != PARS_DYING) {
+ boid_body(&bbd, pa);
+
+ /* deflection */
+ if (sim->colliders)
+ collision_check(sim, p, pa->state.time, cfra);
+ }
+ }
+ break;
+ }
+ case PART_PHYS_FLUID:
+ {
+ SPHData sphdata;
+ psys_sph_init(sim, &sphdata);
+
+ DynamicStepSolverTaskData task_data = {
+ .sim = sim, .cfra = cfra, .timestep = timestep, .dtime = dtime,
+ };
+
+ BLI_spin_init(&task_data.spin);
+
+ if (part->fluid->solver == SPH_SOLVER_DDR) {
+ /* Apply SPH forces using double-density relaxation algorithm
+ * (Clavat et. al.) */
+
+ BLI_task_parallel_range_ex(
+ 0, psys->totpart, &task_data, &sphdata, sizeof(sphdata),
+ dynamics_step_sph_ddr_task_cb_ex, psys->totpart > 100, true);
+
+ sph_springs_modify(psys, timestep);
+ }
+ else {
+ /* SPH_SOLVER_CLASSICAL */
+ /* Apply SPH forces using classical algorithm (due to Gingold
+ * and Monaghan). Note that, unlike double-density relaxation,
+ * this algorithm is separated into distinct loops. */
+
+ BLI_task_parallel_range_ex(
+ 0, psys->totpart, &task_data, NULL, 0,
+ dynamics_step_sph_classical_basic_integrate_task_cb_ex, psys->totpart > 100, true);
+
+ /* calculate summation density */
+ /* Note that we could avoid copying sphdata for each thread here (it's only read here),
+ * but doubt this would gain us anything except confusion... */
+ BLI_task_parallel_range_ex(
+ 0, psys->totpart, &task_data, &sphdata, sizeof(sphdata),
+ dynamics_step_sph_classical_calc_density_task_cb_ex, psys->totpart > 100, true);
+
+ /* do global forces & effectors */
+ BLI_task_parallel_range_ex(
+ 0, psys->totpart, &task_data, &sphdata, sizeof(sphdata),
+ dynamics_step_sph_classical_integrate_task_cb_ex, psys->totpart > 100, true);
+ }
+
+ BLI_spin_end(&task_data.spin);
+
+ psys_sph_finalise(&sphdata);
+ break;
+ }
+ }
+
+ /* finalize particle state and time after dynamics */
+ LOOP_DYNAMIC_PARTICLES {
+ if (pa->alive == PARS_DYING) {
+ pa->alive=PARS_DEAD;
+ pa->state.time=pa->dietime;
+ }
+ else
+ pa->state.time=cfra;
+ }
+
+ free_collider_cache(&sim->colliders);
+ BLI_rng_free(rng);
+}
+static void update_children(ParticleSimulationData *sim)
+{
+ if ((sim->psys->part->type == PART_HAIR) && (sim->psys->flag & PSYS_HAIR_DONE)==0)
+ /* don't generate children while growing hair - waste of time */
+ psys_free_children(sim->psys);
+ else if (sim->psys->part->childtype) {
+ if (sim->psys->totchild != psys_get_tot_child(sim->scene, sim->psys))
+ distribute_particles(sim, PART_FROM_CHILD);
+ else {
+ /* Children are up to date, nothing to do. */
+ }
+ }
+ else
+ psys_free_children(sim->psys);
+}
+/* updates cached particles' alive & other flags etc..*/
+static void cached_step(ParticleSimulationData *sim, float cfra)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ ParticleTexture ptex;
+ PARTICLE_P;
+ float disp, dietime;
+
+ psys_update_effectors(sim);
+
+ disp= psys_get_current_display_percentage(psys);
+
+ LOOP_PARTICLES {
+ psys_get_texture(sim, pa, &ptex, PAMAP_SIZE, cfra);
+ pa->size = part->size*ptex.size;
+ if (part->randsize > 0.0f)
+ pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(sim);
+
+ dietime = pa->dietime;
+
+ /* update alive status and push events */
+ if (pa->time > cfra) {
+ pa->alive = PARS_UNBORN;
+ if (part->flag & PART_UNBORN && (psys->pointcache->flag & PTCACHE_EXTERNAL) == 0)
+ reset_particle(sim, pa, 0.0f, cfra);
+ }
+ else if (dietime <= cfra)
+ pa->alive = PARS_DEAD;
+ else
+ pa->alive = PARS_ALIVE;
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (psys_frand(psys, p) > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+}
+
+static void particles_fluid_step(ParticleSimulationData *sim, int UNUSED(cfra), const bool use_render_params)
+{
+ ParticleSystem *psys = sim->psys;
+ if (psys->particles) {
+ MEM_freeN(psys->particles);
+ psys->particles = 0;
+ psys->totpart = 0;
+ }
+
+ /* fluid sim particle import handling, actual loading of particles from file */
+#ifdef WITH_MOD_FLUID
+ {
+ FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(sim->ob, eModifierType_Fluidsim);
+
+ if ( fluidmd && fluidmd->fss) {
+ FluidsimSettings *fss= fluidmd->fss;
+ ParticleSettings *part = psys->part;
+ ParticleData *pa=NULL;
+ char filename[256];
+ char debugStrBuffer[256];
+ int curFrame = sim->scene->r.cfra -1; // warning - sync with derived mesh fsmesh loading
+ int p, j, totpart;
+ int readMask, activeParts = 0, fileParts = 0;
+ gzFile gzf;
+
+// XXX if (ob==G.obedit) // off...
+// return;
+
+ // ok, start loading
+ BLI_join_dirfile(filename, sizeof(filename), fss->surfdataPath, OB_FLUIDSIM_SURF_PARTICLES_FNAME);
+
+ BLI_path_abs(filename, modifier_path_relbase(sim->ob));
+
+ BLI_path_frame(filename, curFrame, 0); // fixed #frame-no
+
+ gzf = BLI_gzopen(filename, "rb");
+ if (!gzf) {
+ BLI_snprintf(debugStrBuffer, sizeof(debugStrBuffer),"readFsPartData::error - Unable to open file for reading '%s'\n", filename);
+ // XXX bad level call elbeemDebugOut(debugStrBuffer);
+ return;
+ }
+
+ gzread(gzf, &totpart, sizeof(totpart));
+ totpart = (use_render_params) ? totpart:(part->disp*totpart) / 100;
+
+ part->totpart= totpart;
+ part->sta=part->end = 1.0f;
+ part->lifetime = sim->scene->r.efra + 1;
+
+ /* allocate particles */
+ realloc_particles(sim, part->totpart);
+
+ // set up reading mask
+ readMask = fss->typeFlags;
+
+ for (p=0, pa=psys->particles; p<totpart; p++, pa++) {
+ int ptype=0;
+
+ gzread(gzf, &ptype, sizeof( ptype ));
+ if (ptype & readMask) {
+ activeParts++;
+
+ gzread(gzf, &(pa->size), sizeof(float));
+
+ pa->size /= 10.0f;
+
+ for (j=0; j<3; j++) {
+ float wrf;
+ gzread(gzf, &wrf, sizeof( wrf ));
+ pa->state.co[j] = wrf;
+ //fprintf(stderr,"Rj%d ",j);
+ }
+ for (j=0; j<3; j++) {
+ float wrf;
+ gzread(gzf, &wrf, sizeof( wrf ));
+ pa->state.vel[j] = wrf;
+ }
+
+ zero_v3(pa->state.ave);
+ unit_qt(pa->state.rot);
+
+ pa->time = 1.f;
+ pa->dietime = sim->scene->r.efra + 1;
+ pa->lifetime = sim->scene->r.efra;
+ pa->alive = PARS_ALIVE;
+ //if (a < 25) fprintf(stderr,"FSPARTICLE debug set %s, a%d = %f,%f,%f, life=%f\n", filename, a, pa->co[0],pa->co[1],pa->co[2], pa->lifetime );
+ }
+ else {
+ // skip...
+ for (j=0; j<2*3+1; j++) {
+ float wrf; gzread(gzf, &wrf, sizeof( wrf ));
+ }
+ }
+ fileParts++;
+ }
+ gzclose(gzf);
+
+ totpart = psys->totpart = activeParts;
+ BLI_snprintf(debugStrBuffer,sizeof(debugStrBuffer),"readFsPartData::done - particles:%d, active:%d, file:%d, mask:%d\n", psys->totpart,activeParts,fileParts,readMask);
+ // bad level call
+ // XXX elbeemDebugOut(debugStrBuffer);
+
+ } // fluid sim particles done
+ }
+#else
+ UNUSED_VARS(use_render_params);
+#endif // WITH_MOD_FLUID
+}
+
+static int emit_particles(ParticleSimulationData *sim, PTCacheID *pid, float UNUSED(cfra))
+{
+ ParticleSystem *psys = sim->psys;
+ int oldtotpart = psys->totpart;
+ int totpart = tot_particles(psys, pid);
+
+ if (totpart != oldtotpart)
+ realloc_particles(sim, totpart);
+
+ return totpart - oldtotpart;
+}
+
+/* Calculates the next state for all particles of the system
+ * In particles code most fra-ending are frames, time-ending are fra*timestep (seconds)
+ * 1. Emit particles
+ * 2. Check cache (if used) and return if frame is cached
+ * 3. Do dynamics
+ * 4. Save to cache */
+static void system_step(ParticleSimulationData *sim, float cfra, const bool use_render_params)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ PointCache *cache = psys->pointcache;
+ PTCacheID ptcacheid, *pid = NULL;
+ PARTICLE_P;
+ float disp, cache_cfra = cfra; /*, *vg_vel= 0, *vg_tan= 0, *vg_rot= 0, *vg_size= 0; */
+ int startframe = 0, endframe = 100, oldtotpart = 0;
+
+ /* cache shouldn't be used for hair or "continue physics" */
+ if (part->type != PART_HAIR) {
+ psys_clear_temp_pointcache(psys);
+
+ /* set suitable cache range automatically */
+ if ((cache->flag & (PTCACHE_BAKING|PTCACHE_BAKED))==0)
+ psys_get_pointcache_start_end(sim->scene, psys, &cache->startframe, &cache->endframe);
+
+ pid = &ptcacheid;
+ BKE_ptcache_id_from_particles(pid, sim->ob, psys);
+
+ BKE_ptcache_id_time(pid, sim->scene, 0.0f, &startframe, &endframe, NULL);
+
+ /* clear everything on start frame, or when psys needs full reset! */
+ if ((cfra == startframe) || (psys->recalc & PSYS_RECALC_RESET)) {
+ BKE_ptcache_id_reset(sim->scene, pid, PTCACHE_RESET_OUTDATED);
+ BKE_ptcache_validate(cache, startframe);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+ }
+
+ CLAMP(cache_cfra, startframe, endframe);
+ }
+
+/* 1. emit particles and redo particles if needed */
+ oldtotpart = psys->totpart;
+ if (emit_particles(sim, pid, cfra) || psys->recalc & PSYS_RECALC_RESET) {
+ distribute_particles(sim, part->from);
+ initialize_all_particles(sim);
+ /* reset only just created particles (on startframe all particles are recreated) */
+ reset_all_particles(sim, 0.0, cfra, oldtotpart);
+ free_unexisting_particles(sim);
+
+ if (psys->fluid_springs) {
+ MEM_freeN(psys->fluid_springs);
+ psys->fluid_springs = NULL;
+ }
+
+ psys->tot_fluidsprings = psys->alloc_fluidsprings = 0;
+
+ /* flag for possible explode modifiers after this system */
+ sim->psmd->flag |= eParticleSystemFlag_Pars;
+
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, cfra);
+ }
+
+/* 2. try to read from the cache */
+ if (pid) {
+ int cache_result = BKE_ptcache_read(pid, cache_cfra, true);
+
+ if (ELEM(cache_result, PTCACHE_READ_EXACT, PTCACHE_READ_INTERPOLATED)) {
+ cached_step(sim, cfra);
+ update_children(sim);
+ psys_update_path_cache(sim, cfra, use_render_params);
+
+ BKE_ptcache_validate(cache, (int)cache_cfra);
+
+ if (cache_result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED)
+ BKE_ptcache_write(pid, (int)cache_cfra);
+
+ return;
+ }
+ /* Cache is supposed to be baked, but no data was found so bail out */
+ else if (cache->flag & PTCACHE_BAKED) {
+ psys_reset(psys, PSYS_RESET_CACHE_MISS);
+ return;
+ }
+ else if (cache_result == PTCACHE_READ_OLD) {
+ psys->cfra = (float)cache->simframe;
+ cached_step(sim, psys->cfra);
+ }
+
+ /* if on second frame, write cache for first frame */
+ if (psys->cfra == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0))
+ BKE_ptcache_write(pid, startframe);
+ }
+ else
+ BKE_ptcache_invalidate(cache);
+
+/* 3. do dynamics */
+ /* set particles to be not calculated TODO: can't work with pointcache */
+ disp= psys_get_current_display_percentage(psys);
+
+ LOOP_PARTICLES {
+ if (psys_frand(psys, p) > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+
+ if (psys->totpart) {
+ int dframe, totframesback = 0;
+ float t_frac, dt_frac;
+
+ /* handle negative frame start at the first frame by doing
+ * all the steps before the first frame */
+ if ((int)cfra == startframe && part->sta < startframe)
+ totframesback = (startframe - (int)part->sta);
+
+ if (!(part->time_flag & PART_TIME_AUTOSF)) {
+ /* Constant time step */
+ psys->dt_frac = get_base_time_step(part);
+ }
+ else if ((int)cfra == startframe) {
+ /* Variable time step; initialise to subframes */
+ psys->dt_frac = get_base_time_step(part);
+ }
+ else if (psys->dt_frac < MIN_TIMESTEP) {
+ /* Variable time step; subsequent frames */
+ psys->dt_frac = MIN_TIMESTEP;
+ }
+
+ for (dframe=-totframesback; dframe<=0; dframe++) {
+ /* simulate each subframe */
+ dt_frac = psys->dt_frac;
+ for (t_frac = dt_frac; t_frac <= 1.0f; t_frac += dt_frac) {
+ sim->courant_num = 0.0f;
+ dynamics_step(sim, cfra+dframe+t_frac - 1.f);
+ psys->cfra = cfra+dframe+t_frac - 1.f;
+#if 0
+ printf("%f,%f,%f,%f\n", cfra+dframe+t_frac - 1.f, t_frac, dt_frac, sim->courant_num);
+#endif
+ if (part->time_flag & PART_TIME_AUTOSF)
+ update_timestep(psys, sim);
+ /* Even without AUTOSF dt_frac may not add up to 1.0 due to float precision. */
+ dt_frac = sync_timestep(psys, t_frac);
+ }
+ }
+ }
+
+/* 4. only write cache starting from second frame */
+ if (pid) {
+ BKE_ptcache_validate(cache, (int)cache_cfra);
+ if ((int)cache_cfra != startframe)
+ BKE_ptcache_write(pid, (int)cache_cfra);
+ }
+
+ update_children(sim);
+
+/* cleanup */
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+}
+
+/* system type has changed so set sensible defaults and clear non applicable flags */
+void psys_changed_type(Object *ob, ParticleSystem *psys)
+{
+ ParticleSettings *part = psys->part;
+ PTCacheID pid;
+
+ BKE_ptcache_id_from_particles(&pid, ob, psys);
+
+ if (part->phystype != PART_PHYS_KEYED)
+ psys->flag &= ~PSYS_KEYED;
+
+ if (part->type == PART_HAIR) {
+ if (ELEM(part->ren_as, PART_DRAW_NOT, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR)==0)
+ part->ren_as = PART_DRAW_PATH;
+
+ if (part->distr == PART_DISTR_GRID)
+ part->distr = PART_DISTR_JIT;
+
+ if (ELEM(part->draw_as, PART_DRAW_NOT, PART_DRAW_REND, PART_DRAW_PATH)==0)
+ part->draw_as = PART_DRAW_REND;
+
+ CLAMP(part->path_start, 0.0f, 100.0f);
+ CLAMP(part->path_end, 0.0f, 100.0f);
+
+ BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_ALL, 0);
+ }
+ else {
+ free_hair(ob, psys, 1);
+
+ CLAMP(part->path_start, 0.0f, MAX2(100.0f, part->end + part->lifetime));
+ CLAMP(part->path_end, 0.0f, MAX2(100.0f, part->end + part->lifetime));
+ }
+
+ psys_reset(psys, PSYS_RESET_ALL);
+}
+void psys_check_boid_data(ParticleSystem *psys)
+{
+ BoidParticle *bpa;
+ PARTICLE_P;
+
+ pa = psys->particles;
+
+ if (!pa)
+ return;
+
+ if (psys->part && psys->part->phystype==PART_PHYS_BOIDS) {
+ if (!pa->boid) {
+ bpa = MEM_callocN(psys->totpart * sizeof(BoidParticle), "Boid Data");
+
+ LOOP_PARTICLES
+ pa->boid = bpa++;
+ }
+ }
+ else if (pa->boid) {
+ MEM_freeN(pa->boid);
+ LOOP_PARTICLES
+ pa->boid = NULL;
+ }
+}
+
+static void fluid_default_settings(ParticleSettings *part)
+{
+ SPHFluidSettings *fluid = part->fluid;
+
+ fluid->spring_k = 0.f;
+ fluid->plasticity_constant = 0.1f;
+ fluid->yield_ratio = 0.1f;
+ fluid->rest_length = 1.f;
+ fluid->viscosity_omega = 2.f;
+ fluid->viscosity_beta = 0.1f;
+ fluid->stiffness_k = 1.f;
+ fluid->stiffness_knear = 1.f;
+ fluid->rest_density = 1.f;
+ fluid->buoyancy = 0.f;
+ fluid->radius = 1.f;
+ fluid->flag |= SPH_FAC_REPULSION|SPH_FAC_DENSITY|SPH_FAC_RADIUS|SPH_FAC_VISCOSITY|SPH_FAC_REST_LENGTH;
+}
+
+static void psys_prepare_physics(ParticleSimulationData *sim)
+{
+ ParticleSettings *part = sim->psys->part;
+
+ if (ELEM(part->phystype, PART_PHYS_NO, PART_PHYS_KEYED)) {
+ PTCacheID pid;
+ BKE_ptcache_id_from_particles(&pid, sim->ob, sim->psys);
+ BKE_ptcache_id_clear(&pid, PTCACHE_CLEAR_ALL, 0);
+ }
+ else {
+ free_keyed_keys(sim->psys);
+ sim->psys->flag &= ~PSYS_KEYED;
+ }
+
+ if (part->phystype == PART_PHYS_BOIDS && part->boids == NULL) {
+ BoidState *state;
+
+ part->boids = MEM_callocN(sizeof(BoidSettings), "Boid Settings");
+ boid_default_settings(part->boids);
+
+ state = boid_new_state(part->boids);
+ BLI_addtail(&state->rules, boid_new_rule(eBoidRuleType_Separate));
+ BLI_addtail(&state->rules, boid_new_rule(eBoidRuleType_Flock));
+
+ ((BoidRule*)state->rules.first)->flag |= BOIDRULE_CURRENT;
+
+ state->flag |= BOIDSTATE_CURRENT;
+ BLI_addtail(&part->boids->states, state);
+ }
+ else if (part->phystype == PART_PHYS_FLUID && part->fluid == NULL) {
+ part->fluid = MEM_callocN(sizeof(SPHFluidSettings), "SPH Fluid Settings");
+ fluid_default_settings(part);
+ }
+
+ psys_check_boid_data(sim->psys);
+}
+static int hair_needs_recalc(ParticleSystem *psys)
+{
+ if (!(psys->flag & PSYS_EDITED) && (!psys->edit || !psys->edit->edited) &&
+ ((psys->flag & PSYS_HAIR_DONE)==0 || psys->recalc & PSYS_RECALC_RESET || (psys->part->flag & PART_HAIR_REGROW && !psys->edit)))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* main particle update call, checks that things are ok on the large scale and
+ * then advances in to actual particle calculations depending on particle type */
+void particle_system_update(Scene *scene, Object *ob, ParticleSystem *psys, const bool use_render_params)
+{
+ ParticleSimulationData sim= {0};
+ ParticleSettings *part = psys->part;
+ float cfra;
+
+ /* drawdata is outdated after ANY change */
+ if (psys->pdd) psys->pdd->flag &= ~PARTICLE_DRAW_DATA_UPDATED;
+
+ if (!psys_check_enabled(ob, psys, use_render_params))
+ return;
+
+ cfra= BKE_scene_frame_get(scene);
+
+ sim.scene= scene;
+ sim.ob= ob;
+ sim.psys= psys;
+ sim.psmd= psys_get_modifier(ob, psys);
+
+ /* system was already updated from modifier stack */
+ if (sim.psmd->flag & eParticleSystemFlag_psys_updated) {
+ sim.psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ /* make sure it really was updated to cfra */
+ if (psys->cfra == cfra)
+ return;
+ }
+
+ if (!sim.psmd->dm_final)
+ return;
+
+ if (part->from != PART_FROM_VERT) {
+ DM_ensure_tessface(sim.psmd->dm_final);
+ }
+
+ /* execute drivers only, as animation has already been done */
+ BKE_animsys_evaluate_animdata(scene, &part->id, part->adt, cfra, ADT_RECALC_DRIVERS);
+
+ /* to verify if we need to restore object afterwards */
+ psys->flag &= ~PSYS_OB_ANIM_RESTORE;
+
+ if (psys->recalc & PSYS_RECALC_TYPE)
+ psys_changed_type(sim.ob, sim.psys);
+
+ if (psys->recalc & PSYS_RECALC_RESET)
+ psys->totunexist = 0;
+
+ /* setup necessary physics type dependent additional data if it doesn't yet exist */
+ psys_prepare_physics(&sim);
+
+ switch (part->type) {
+ case PART_HAIR:
+ {
+ /* nothing to do so bail out early */
+ if (psys->totpart == 0 && part->totpart == 0) {
+ psys_free_path_cache(psys, NULL);
+ free_hair(ob, psys, 0);
+ psys->flag |= PSYS_HAIR_DONE;
+ }
+ /* (re-)create hair */
+ else if (hair_needs_recalc(psys)) {
+ float hcfra=0.0f;
+ int i, recalc = psys->recalc;
+
+ free_hair(ob, psys, 0);
+
+ if (psys->edit && psys->free_edit) {
+ psys->free_edit(psys->edit);
+ psys->edit = NULL;
+ psys->free_edit = NULL;
+ }
+
+ /* first step is negative so particles get killed and reset */
+ psys->cfra= 1.0f;
+
+ for (i=0; i<=part->hair_step; i++) {
+ hcfra=100.0f*(float)i/(float)psys->part->hair_step;
+ if ((part->flag & PART_HAIR_REGROW)==0)
+ BKE_animsys_evaluate_animdata(scene, &part->id, part->adt, hcfra, ADT_RECALC_ANIM);
+ system_step(&sim, hcfra, use_render_params);
+ psys->cfra = hcfra;
+ psys->recalc = 0;
+ save_hair(&sim, hcfra);
+ }
+
+ psys->flag |= PSYS_HAIR_DONE;
+ psys->recalc = recalc;
+ }
+ else if (psys->flag & PSYS_EDITED)
+ psys->flag |= PSYS_HAIR_DONE;
+
+ if (psys->flag & PSYS_HAIR_DONE)
+ hair_step(&sim, cfra, use_render_params);
+ break;
+ }
+ case PART_FLUID:
+ {
+ particles_fluid_step(&sim, (int)cfra, use_render_params);
+ break;
+ }
+ default:
+ {
+ switch (part->phystype) {
+ case PART_PHYS_NO:
+ case PART_PHYS_KEYED:
+ {
+ PARTICLE_P;
+ float disp = psys_get_current_display_percentage(psys);
+ bool free_unexisting = false;
+
+ /* Particles without dynamics haven't been reset yet because they don't use pointcache */
+ if (psys->recalc & PSYS_RECALC_RESET)
+ psys_reset(psys, PSYS_RESET_ALL);
+
+ if (emit_particles(&sim, NULL, cfra) || (psys->recalc & PSYS_RECALC_RESET)) {
+ free_keyed_keys(psys);
+ distribute_particles(&sim, part->from);
+ initialize_all_particles(&sim);
+ free_unexisting = true;
+
+ /* flag for possible explode modifiers after this system */
+ sim.psmd->flag |= eParticleSystemFlag_Pars;
+ }
+
+ LOOP_EXISTING_PARTICLES {
+ pa->size = part->size;
+ if (part->randsize > 0.0f)
+ pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
+
+ reset_particle(&sim, pa, 0.0, cfra);
+
+ if (psys_frand(psys, p) > disp)
+ pa->flag |= PARS_NO_DISP;
+ else
+ pa->flag &= ~PARS_NO_DISP;
+ }
+
+ /* free unexisting after reseting particles */
+ if (free_unexisting)
+ free_unexisting_particles(&sim);
+
+ if (part->phystype == PART_PHYS_KEYED) {
+ psys_count_keyed_targets(&sim);
+ set_keyed_keys(&sim);
+ psys_update_path_cache(&sim, (int)cfra, use_render_params);
+ }
+ break;
+ }
+ default:
+ {
+ /* the main dynamic particle system step */
+ system_step(&sim, cfra, use_render_params);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ /* make sure emitter is left at correct time (particle emission can change this) */
+ if (psys->flag & PSYS_OB_ANIM_RESTORE) {
+ evaluate_emitter_anim(scene, ob, cfra);
+ psys->flag &= ~PSYS_OB_ANIM_RESTORE;
+ }
+
+ psys->cfra = cfra;
+ psys->recalc = 0;
+
+ /* save matrix for duplicators, at rendertime the actual dupliobject's matrix is used so don't update! */
+ if (psys->renderdata==0)
+ invert_m4_m4(psys->imat, ob->obmat);
+}
+
+/* ID looper */
+
+void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata)
+{
+ ParticleTarget *pt;
+
+ func(psys, (ID **)&psys->part, userdata, IDWALK_USER | IDWALK_NEVER_NULL);
+ func(psys, (ID **)&psys->target_ob, userdata, IDWALK_NOP);
+ func(psys, (ID **)&psys->parent, userdata, IDWALK_NOP);
+
+ for (pt = psys->targets.first; pt; pt = pt->next) {
+ func(psys, (ID **)&pt->ob, userdata, IDWALK_NOP);
+ }
+
+ /* Even though psys->part should never be NULL, this can happen as an exception during deletion.
+ * See ID_REMAP_SKIP/FORCE/FLAG_NEVER_NULL_USAGE in BKE_library_remap. */
+ if (psys->part && psys->part->phystype == PART_PHYS_BOIDS) {
+ ParticleData *pa;
+ int p;
+
+ for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) {
+ func(psys, (ID **)&pa->boid->ground, userdata, IDWALK_NOP);
+ }
+ }
+}
+
+/* **** Depsgraph evaluation **** */
+
+void BKE_particle_system_eval(EvaluationContext *UNUSED(eval_ctx),
+ Scene *scene,
+ Object *ob,
+ ParticleSystem *psys)
+{
+ if (G.debug & G_DEBUG_DEPSGRAPH) {
+ printf("%s on %s:%s\n", __func__, ob->id.name, psys->name);
+ }
+ BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_DEPSGRAPH);
+}
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
new file mode 100644
index 00000000000..30eb8dcb287
--- /dev/null
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -0,0 +1,4095 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Campbell Barton <ideasman42@gmail.com>
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/pointcache.c
+ * \ingroup bke
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_dynamicpaint_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
+#include "DNA_rigidbody_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_smoke_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_threads.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "BKE_appdir.h"
+#include "BKE_anim.h"
+#include "BKE_cloth.h"
+#include "BKE_dynamicpaint.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_scene.h"
+#include "BKE_smoke.h"
+#include "BKE_softbody.h"
+
+#include "BIK_api.h"
+
+#ifdef WITH_BULLET
+# include "RBI_api.h"
+#endif
+
+/* both in intern */
+#ifdef WITH_SMOKE
+#include "smoke_API.h"
+#endif
+
+#ifdef WITH_OPENVDB
+#include "openvdb_capi.h"
+#endif
+
+#ifdef WITH_LZO
+# ifdef WITH_SYSTEM_LZO
+# include <lzo/lzo1x.h>
+# else
+# include "minilzo.h"
+# endif
+# define LZO_HEAP_ALLOC(var,size) \
+ lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
+#endif
+
+#define LZO_OUT_LEN(size) ((size) + (size) / 16 + 64 + 3)
+
+#ifdef WITH_LZMA
+#include "LzmaLib.h"
+#endif
+
+/* needed for directory lookup */
+#ifndef WIN32
+# include <dirent.h>
+#else
+# include "BLI_winstuff.h"
+#endif
+
+#define PTCACHE_DATA_FROM(data, type, from) \
+ if (data[type]) { \
+ memcpy(data[type], from, ptcache_data_size[type]); \
+ } (void)0
+
+#define PTCACHE_DATA_TO(data, type, index, to) \
+ if (data[type]) { \
+ memcpy(to, (char *)(data)[type] + ((index) ? (index) * ptcache_data_size[type] : 0), ptcache_data_size[type]); \
+ } (void)0
+
+/* could be made into a pointcache option */
+#define DURIAN_POINTCACHE_LIB_OK 1
+
+static int ptcache_data_size[] = {
+ sizeof(unsigned int), // BPHYS_DATA_INDEX
+ 3 * sizeof(float), // BPHYS_DATA_LOCATION
+ 3 * sizeof(float), // BPHYS_DATA_VELOCITY
+ 4 * sizeof(float), // BPHYS_DATA_ROTATION
+ 3 * sizeof(float), // BPHYS_DATA_AVELOCITY / BPHYS_DATA_XCONST
+ sizeof(float), // BPHYS_DATA_SIZE
+ 3 * sizeof(float), // BPHYS_DATA_TIMES
+ sizeof(BoidData) // case BPHYS_DATA_BOIDS
+};
+
+static int ptcache_extra_datasize[] = {
+ 0,
+ sizeof(ParticleSpring)
+};
+
+/* forward declerations */
+static int ptcache_file_compressed_read(PTCacheFile *pf, unsigned char *result, unsigned int len);
+static int ptcache_file_compressed_write(PTCacheFile *pf, unsigned char *in, unsigned int in_len, unsigned char *out, int mode);
+static int ptcache_file_write(PTCacheFile *pf, const void *f, unsigned int tot, unsigned int size);
+static int ptcache_file_read(PTCacheFile *pf, void *f, unsigned int tot, unsigned int size);
+
+/* Common functions */
+static int ptcache_basic_header_read(PTCacheFile *pf)
+{
+ int error=0;
+
+ /* Custom functions should read these basic elements too! */
+ if (!error && !fread(&pf->totpoint, sizeof(unsigned int), 1, pf->fp))
+ error = 1;
+
+ if (!error && !fread(&pf->data_types, sizeof(unsigned int), 1, pf->fp))
+ error = 1;
+
+ return !error;
+}
+static int ptcache_basic_header_write(PTCacheFile *pf)
+{
+ /* Custom functions should write these basic elements too! */
+ if (!fwrite(&pf->totpoint, sizeof(unsigned int), 1, pf->fp))
+ return 0;
+
+ if (!fwrite(&pf->data_types, sizeof(unsigned int), 1, pf->fp))
+ return 0;
+
+ return 1;
+}
+/* Softbody functions */
+static int ptcache_softbody_write(int index, void *soft_v, void **data, int UNUSED(cfra))
+{
+ SoftBody *soft= soft_v;
+ BodyPoint *bp = soft->bpoint + index;
+
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, bp->pos);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_VELOCITY, bp->vec);
+
+ return 1;
+}
+static void ptcache_softbody_read(int index, void *soft_v, void **data, float UNUSED(cfra), float *old_data)
+{
+ SoftBody *soft= soft_v;
+ BodyPoint *bp = soft->bpoint + index;
+
+ if (old_data) {
+ memcpy(bp->pos, data, 3 * sizeof(float));
+ memcpy(bp->vec, data + 3, 3 * sizeof(float));
+ }
+ else {
+ PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, bp->pos);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, bp->vec);
+ }
+}
+static void ptcache_softbody_interpolate(int index, void *soft_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+{
+ SoftBody *soft= soft_v;
+ BodyPoint *bp = soft->bpoint + index;
+ ParticleKey keys[4];
+ float dfra;
+
+ if (cfra1 == cfra2)
+ return;
+
+ copy_v3_v3(keys[1].co, bp->pos);
+ copy_v3_v3(keys[1].vel, bp->vec);
+
+ if (old_data) {
+ memcpy(keys[2].co, old_data, 3 * sizeof(float));
+ memcpy(keys[2].vel, old_data + 3, 3 * sizeof(float));
+ }
+ else
+ BKE_ptcache_make_particle_key(keys+2, 0, data, cfra2);
+
+ dfra = cfra2 - cfra1;
+
+ mul_v3_fl(keys[1].vel, dfra);
+ mul_v3_fl(keys[2].vel, dfra);
+
+ psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, keys, 1);
+
+ mul_v3_fl(keys->vel, 1.0f / dfra);
+
+ copy_v3_v3(bp->pos, keys->co);
+ copy_v3_v3(bp->vec, keys->vel);
+}
+static int ptcache_softbody_totpoint(void *soft_v, int UNUSED(cfra))
+{
+ SoftBody *soft= soft_v;
+ return soft->totpoint;
+}
+static void ptcache_softbody_error(void *UNUSED(soft_v), const char *UNUSED(message))
+{
+ /* ignored for now */
+}
+
+/* Particle functions */
+void BKE_ptcache_make_particle_key(ParticleKey *key, int index, void **data, float time)
+{
+ PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, index, key->co);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, index, key->vel);
+
+ /* no rotation info, so make something nice up */
+ if (data[BPHYS_DATA_ROTATION]==NULL) {
+ vec_to_quat(key->rot, key->vel, OB_NEGX, OB_POSZ);
+ }
+ else {
+ PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, index, key->rot);
+ }
+
+ PTCACHE_DATA_TO(data, BPHYS_DATA_AVELOCITY, index, key->ave);
+ key->time = time;
+}
+static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra)
+{
+ ParticleSystem *psys= psys_v;
+ ParticleData *pa = psys->particles + index;
+ BoidParticle *boid = (psys->part->phystype == PART_PHYS_BOIDS) ? pa->boid : NULL;
+ float times[3];
+ int step = psys->pointcache->step;
+
+ /* No need to store unborn or died particles outside cache step bounds */
+ if (data[BPHYS_DATA_INDEX] && (cfra < pa->time - step || cfra > pa->dietime + step))
+ return 0;
+
+ times[0] = pa->time;
+ times[1] = pa->dietime;
+ times[2] = pa->lifetime;
+
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_INDEX, &index);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, pa->state.co);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_VELOCITY, pa->state.vel);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_ROTATION, pa->state.rot);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_AVELOCITY, pa->state.ave);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_SIZE, &pa->size);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_TIMES, times);
+
+ if (boid) {
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_BOIDS, &boid->data);
+ }
+
+ /* return flag 1+1=2 for newly born particles to copy exact birth location to previously cached frame */
+ return 1 + (pa->state.time >= pa->time && pa->prev_state.time <= pa->time);
+}
+static void ptcache_particle_read(int index, void *psys_v, void **data, float cfra, float *old_data)
+{
+ ParticleSystem *psys= psys_v;
+ ParticleData *pa;
+ BoidParticle *boid;
+ float timestep = 0.04f * psys->part->timetweak;
+
+ if (index >= psys->totpart)
+ return;
+
+ pa = psys->particles + index;
+ boid = (psys->part->phystype == PART_PHYS_BOIDS) ? pa->boid : NULL;
+
+ if (cfra > pa->state.time)
+ memcpy(&pa->prev_state, &pa->state, sizeof(ParticleKey));
+
+ if (old_data) {
+ /* old format cache */
+ memcpy(&pa->state, old_data, sizeof(ParticleKey));
+ return;
+ }
+
+ BKE_ptcache_make_particle_key(&pa->state, 0, data, cfra);
+
+ /* set frames cached before birth to birth time */
+ if (cfra < pa->time)
+ pa->state.time = pa->time;
+ else if (cfra > pa->dietime)
+ pa->state.time = pa->dietime;
+
+ if (data[BPHYS_DATA_SIZE]) {
+ PTCACHE_DATA_TO(data, BPHYS_DATA_SIZE, 0, &pa->size);
+ }
+
+ if (data[BPHYS_DATA_TIMES]) {
+ float times[3];
+ PTCACHE_DATA_TO(data, BPHYS_DATA_TIMES, 0, &times);
+ pa->time = times[0];
+ pa->dietime = times[1];
+ pa->lifetime = times[2];
+ }
+
+ if (boid) {
+ PTCACHE_DATA_TO(data, BPHYS_DATA_BOIDS, 0, &boid->data);
+ }
+
+ /* determine velocity from previous location */
+ if (data[BPHYS_DATA_LOCATION] && !data[BPHYS_DATA_VELOCITY]) {
+ if (cfra > pa->prev_state.time) {
+ sub_v3_v3v3(pa->state.vel, pa->state.co, pa->prev_state.co);
+ mul_v3_fl(pa->state.vel, (cfra - pa->prev_state.time) * timestep);
+ }
+ else {
+ sub_v3_v3v3(pa->state.vel, pa->prev_state.co, pa->state.co);
+ mul_v3_fl(pa->state.vel, (pa->prev_state.time - cfra) * timestep);
+ }
+ }
+
+ /* default to no rotation */
+ if (data[BPHYS_DATA_LOCATION] && !data[BPHYS_DATA_ROTATION]) {
+ unit_qt(pa->state.rot);
+ }
+}
+static void ptcache_particle_interpolate(int index, void *psys_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+{
+ ParticleSystem *psys= psys_v;
+ ParticleData *pa;
+ ParticleKey keys[4];
+ float dfra, timestep = 0.04f * psys->part->timetweak;
+
+ if (index >= psys->totpart)
+ return;
+
+ pa = psys->particles + index;
+
+ /* particle wasn't read from first cache so can't interpolate */
+ if ((int)cfra1 < pa->time - psys->pointcache->step || (int)cfra1 > pa->dietime + psys->pointcache->step)
+ return;
+
+ cfra = MIN2(cfra, pa->dietime);
+ cfra1 = MIN2(cfra1, pa->dietime);
+ cfra2 = MIN2(cfra2, pa->dietime);
+
+ if (cfra1 == cfra2)
+ return;
+
+ memcpy(keys+1, &pa->state, sizeof(ParticleKey));
+ if (old_data)
+ memcpy(keys+2, old_data, sizeof(ParticleKey));
+ else
+ BKE_ptcache_make_particle_key(keys+2, 0, data, cfra2);
+
+ /* determine velocity from previous location */
+ if (data[BPHYS_DATA_LOCATION] && !data[BPHYS_DATA_VELOCITY]) {
+ if (keys[1].time > keys[2].time) {
+ sub_v3_v3v3(keys[2].vel, keys[1].co, keys[2].co);
+ mul_v3_fl(keys[2].vel, (keys[1].time - keys[2].time) * timestep);
+ }
+ else {
+ sub_v3_v3v3(keys[2].vel, keys[2].co, keys[1].co);
+ mul_v3_fl(keys[2].vel, (keys[2].time - keys[1].time) * timestep);
+ }
+ }
+
+ /* default to no rotation */
+ if (data[BPHYS_DATA_LOCATION] && !data[BPHYS_DATA_ROTATION]) {
+ unit_qt(keys[2].rot);
+ }
+
+ if (cfra > pa->time)
+ cfra1 = MAX2(cfra1, pa->time);
+
+ dfra = cfra2 - cfra1;
+
+ mul_v3_fl(keys[1].vel, dfra * timestep);
+ mul_v3_fl(keys[2].vel, dfra * timestep);
+
+ psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, &pa->state, 1);
+ interp_qt_qtqt(pa->state.rot, keys[1].rot, keys[2].rot, (cfra - cfra1) / dfra);
+
+ mul_v3_fl(pa->state.vel, 1.f / (dfra * timestep));
+
+ pa->state.time = cfra;
+}
+
+static int ptcache_particle_totpoint(void *psys_v, int UNUSED(cfra))
+{
+ ParticleSystem *psys = psys_v;
+ return psys->totpart;
+}
+
+static void ptcache_particle_error(void *UNUSED(psys_v), const char *UNUSED(message))
+{
+ /* ignored for now */
+}
+
+static int ptcache_particle_totwrite(void *psys_v, int cfra)
+{
+ ParticleSystem *psys = psys_v;
+ ParticleData *pa= psys->particles;
+ int p, step = psys->pointcache->step;
+ int totwrite = 0;
+
+ if (cfra == 0)
+ return psys->totpart;
+
+ for (p=0; p<psys->totpart; p++, pa++)
+ totwrite += (cfra >= pa->time - step && cfra <= pa->dietime + step);
+
+ return totwrite;
+}
+
+static void ptcache_particle_extra_write(void *psys_v, PTCacheMem *pm, int UNUSED(cfra))
+{
+ ParticleSystem *psys = psys_v;
+ PTCacheExtra *extra = NULL;
+
+ if (psys->part->phystype == PART_PHYS_FLUID &&
+ psys->part->fluid && psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS &&
+ psys->tot_fluidsprings && psys->fluid_springs) {
+
+ extra = MEM_callocN(sizeof(PTCacheExtra), "Point cache: fluid extra data");
+
+ extra->type = BPHYS_EXTRA_FLUID_SPRINGS;
+ extra->totdata = psys->tot_fluidsprings;
+
+ extra->data = MEM_callocN(extra->totdata * ptcache_extra_datasize[extra->type], "Point cache: extra data");
+ memcpy(extra->data, psys->fluid_springs, extra->totdata * ptcache_extra_datasize[extra->type]);
+
+ BLI_addtail(&pm->extradata, extra);
+ }
+}
+
+static void ptcache_particle_extra_read(void *psys_v, PTCacheMem *pm, float UNUSED(cfra))
+{
+ ParticleSystem *psys = psys_v;
+ PTCacheExtra *extra = pm->extradata.first;
+
+ for (; extra; extra=extra->next) {
+ switch (extra->type) {
+ case BPHYS_EXTRA_FLUID_SPRINGS:
+ {
+ if (psys->fluid_springs)
+ MEM_freeN(psys->fluid_springs);
+
+ psys->fluid_springs = MEM_dupallocN(extra->data);
+ psys->tot_fluidsprings = psys->alloc_fluidsprings = extra->totdata;
+ break;
+ }
+ }
+ }
+}
+
+/* Cloth functions */
+static int ptcache_cloth_write(int index, void *cloth_v, void **data, int UNUSED(cfra))
+{
+ ClothModifierData *clmd= cloth_v;
+ Cloth *cloth= clmd->clothObject;
+ ClothVertex *vert = cloth->verts + index;
+
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, vert->x);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_VELOCITY, vert->v);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_XCONST, vert->xconst);
+
+ return 1;
+}
+static void ptcache_cloth_read(int index, void *cloth_v, void **data, float UNUSED(cfra), float *old_data)
+{
+ ClothModifierData *clmd= cloth_v;
+ Cloth *cloth= clmd->clothObject;
+ ClothVertex *vert = cloth->verts + index;
+
+ if (old_data) {
+ memcpy(vert->x, data, 3 * sizeof(float));
+ memcpy(vert->xconst, data + 3, 3 * sizeof(float));
+ memcpy(vert->v, data + 6, 3 * sizeof(float));
+ }
+ else {
+ PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, vert->x);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, vert->v);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_XCONST, 0, vert->xconst);
+ }
+}
+static void ptcache_cloth_interpolate(int index, void *cloth_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+{
+ ClothModifierData *clmd= cloth_v;
+ Cloth *cloth= clmd->clothObject;
+ ClothVertex *vert = cloth->verts + index;
+ ParticleKey keys[4];
+ float dfra;
+
+ if (cfra1 == cfra2)
+ return;
+
+ copy_v3_v3(keys[1].co, vert->x);
+ copy_v3_v3(keys[1].vel, vert->v);
+
+ if (old_data) {
+ memcpy(keys[2].co, old_data, 3 * sizeof(float));
+ memcpy(keys[2].vel, old_data + 6, 3 * sizeof(float));
+ }
+ else
+ BKE_ptcache_make_particle_key(keys+2, 0, data, cfra2);
+
+ dfra = cfra2 - cfra1;
+
+ mul_v3_fl(keys[1].vel, dfra);
+ mul_v3_fl(keys[2].vel, dfra);
+
+ psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, keys, 1);
+
+ mul_v3_fl(keys->vel, 1.0f / dfra);
+
+ copy_v3_v3(vert->x, keys->co);
+ copy_v3_v3(vert->v, keys->vel);
+
+ /* should vert->xconst be interpolated somehow too? - jahka */
+}
+
+static int ptcache_cloth_totpoint(void *cloth_v, int UNUSED(cfra))
+{
+ ClothModifierData *clmd= cloth_v;
+ return clmd->clothObject ? clmd->clothObject->mvert_num : 0;
+}
+
+static void ptcache_cloth_error(void *cloth_v, const char *message)
+{
+ ClothModifierData *clmd= cloth_v;
+ modifier_setError(&clmd->modifier, "%s", message);
+}
+
+#ifdef WITH_SMOKE
+/* Smoke functions */
+static int ptcache_smoke_totpoint(void *smoke_v, int UNUSED(cfra))
+{
+ SmokeModifierData *smd= (SmokeModifierData *)smoke_v;
+ SmokeDomainSettings *sds = smd->domain;
+
+ if (sds->fluid) {
+ return sds->base_res[0]*sds->base_res[1]*sds->base_res[2];
+ }
+ else
+ return 0;
+}
+
+static void ptcache_smoke_error(void *smoke_v, const char *message)
+{
+ SmokeModifierData *smd= (SmokeModifierData *)smoke_v;
+ modifier_setError(&smd->modifier, "%s", message);
+}
+
+#define SMOKE_CACHE_VERSION "1.04"
+
+static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v)
+{
+ SmokeModifierData *smd= (SmokeModifierData *)smoke_v;
+ SmokeDomainSettings *sds = smd->domain;
+ int ret = 0;
+ int fluid_fields = smoke_get_data_flags(sds);
+
+ /* version header */
+ ptcache_file_write(pf, SMOKE_CACHE_VERSION, 4, sizeof(char));
+ ptcache_file_write(pf, &fluid_fields, 1, sizeof(int));
+ ptcache_file_write(pf, &sds->active_fields, 1, sizeof(int));
+ ptcache_file_write(pf, &sds->res, 3, sizeof(int));
+ ptcache_file_write(pf, &sds->dx, 1, sizeof(float));
+
+ if (sds->fluid) {
+ size_t res = sds->res[0]*sds->res[1]*sds->res[2];
+ float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
+ unsigned char *obstacles;
+ unsigned int in_len = sizeof(float)*(unsigned int)res;
+ unsigned char *out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len) * 4, "pointcache_lzo_buffer");
+ //int mode = res >= 1000000 ? 2 : 1;
+ int mode=1; // light
+ if (sds->cache_comp == SM_CACHE_HEAVY) mode=2; // heavy
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat, &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles);
+
+ ptcache_file_compressed_write(pf, (unsigned char *)sds->shadow, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)dens, in_len, out, mode);
+ if (fluid_fields & SM_ACTIVE_HEAT) {
+ ptcache_file_compressed_write(pf, (unsigned char *)heat, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)heatold, in_len, out, mode);
+ }
+ if (fluid_fields & SM_ACTIVE_FIRE) {
+ ptcache_file_compressed_write(pf, (unsigned char *)flame, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)fuel, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)react, in_len, out, mode);
+ }
+ if (fluid_fields & SM_ACTIVE_COLORS) {
+ ptcache_file_compressed_write(pf, (unsigned char *)r, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)g, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)b, in_len, out, mode);
+ }
+ ptcache_file_compressed_write(pf, (unsigned char *)vx, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)vy, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)vz, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)obstacles, (unsigned int)res, out, mode);
+ ptcache_file_write(pf, &dt, 1, sizeof(float));
+ ptcache_file_write(pf, &dx, 1, sizeof(float));
+ ptcache_file_write(pf, &sds->p0, 3, sizeof(float));
+ ptcache_file_write(pf, &sds->p1, 3, sizeof(float));
+ ptcache_file_write(pf, &sds->dp0, 3, sizeof(float));
+ ptcache_file_write(pf, &sds->shift, 3, sizeof(int));
+ ptcache_file_write(pf, &sds->obj_shift_f, 3, sizeof(float));
+ ptcache_file_write(pf, &sds->obmat, 16, sizeof(float));
+ ptcache_file_write(pf, &sds->base_res, 3, sizeof(int));
+ ptcache_file_write(pf, &sds->res_min, 3, sizeof(int));
+ ptcache_file_write(pf, &sds->res_max, 3, sizeof(int));
+ ptcache_file_write(pf, &sds->active_color, 3, sizeof(float));
+
+ MEM_freeN(out);
+
+ ret = 1;
+ }
+
+ if (sds->wt) {
+ int res_big_array[3];
+ int res_big;
+ int res = sds->res[0]*sds->res[1]*sds->res[2];
+ float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
+ unsigned int in_len = sizeof(float)*(unsigned int)res;
+ unsigned int in_len_big;
+ unsigned char *out;
+ int mode;
+
+ smoke_turbulence_get_res(sds->wt, res_big_array);
+ res_big = res_big_array[0]*res_big_array[1]*res_big_array[2];
+ //mode = res_big >= 1000000 ? 2 : 1;
+ mode = 1; // light
+ if (sds->cache_high_comp == SM_CACHE_HEAVY) mode=2; // heavy
+
+ in_len_big = sizeof(float) * (unsigned int)res_big;
+
+ smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+
+ out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len_big), "pointcache_lzo_buffer");
+ ptcache_file_compressed_write(pf, (unsigned char *)dens, in_len_big, out, mode);
+ if (fluid_fields & SM_ACTIVE_FIRE) {
+ ptcache_file_compressed_write(pf, (unsigned char *)flame, in_len_big, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)fuel, in_len_big, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)react, in_len_big, out, mode);
+ }
+ if (fluid_fields & SM_ACTIVE_COLORS) {
+ ptcache_file_compressed_write(pf, (unsigned char *)r, in_len_big, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)g, in_len_big, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)b, in_len_big, out, mode);
+ }
+ MEM_freeN(out);
+
+ out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len), "pointcache_lzo_buffer");
+ ptcache_file_compressed_write(pf, (unsigned char *)tcu, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)tcv, in_len, out, mode);
+ ptcache_file_compressed_write(pf, (unsigned char *)tcw, in_len, out, mode);
+ MEM_freeN(out);
+
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/* read old smoke cache from 2.64 */
+static int ptcache_smoke_read_old(PTCacheFile *pf, void *smoke_v)
+{
+ SmokeModifierData *smd= (SmokeModifierData *)smoke_v;
+ SmokeDomainSettings *sds = smd->domain;
+
+ if (sds->fluid) {
+ const size_t res = sds->res[0] * sds->res[1] * sds->res[2];
+ const unsigned int out_len = (unsigned int)res * sizeof(float);
+ float dt, dx, *dens, *heat, *heatold, *vx, *vy, *vz;
+ unsigned char *obstacles;
+ float *tmp_array = MEM_callocN(out_len, "Smoke old cache tmp");
+
+ int fluid_fields = smoke_get_data_flags(sds);
+
+ /* Part part of the new cache header */
+ sds->active_color[0] = 0.7f;
+ sds->active_color[1] = 0.7f;
+ sds->active_color[2] = 0.7f;
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, NULL, NULL, NULL, &heat, &heatold, &vx, &vy, &vz, NULL, NULL, NULL, &obstacles);
+
+ ptcache_file_compressed_read(pf, (unsigned char *)sds->shadow, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)dens, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array, out_len);
+
+ if (fluid_fields & SM_ACTIVE_HEAT)
+ {
+ ptcache_file_compressed_read(pf, (unsigned char*)heat, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)heatold, out_len);
+ }
+ else
+ {
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array, out_len);
+ }
+ ptcache_file_compressed_read(pf, (unsigned char*)vx, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)vy, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)vz, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)obstacles, (unsigned int)res);
+ ptcache_file_read(pf, &dt, 1, sizeof(float));
+ ptcache_file_read(pf, &dx, 1, sizeof(float));
+
+ MEM_freeN(tmp_array);
+
+ if (pf->data_types & (1<<BPHYS_DATA_SMOKE_HIGH) && sds->wt) {
+ int res_big, res_big_array[3];
+ float *tcu, *tcv, *tcw;
+ unsigned int out_len_big;
+ unsigned char *tmp_array_big;
+ smoke_turbulence_get_res(sds->wt, res_big_array);
+ res_big = res_big_array[0]*res_big_array[1]*res_big_array[2];
+ out_len_big = sizeof(float) * (unsigned int)res_big;
+
+ tmp_array_big = MEM_callocN(out_len_big, "Smoke old cache tmp");
+
+ smoke_turbulence_export(sds->wt, &dens, NULL, NULL, NULL, NULL, NULL, NULL, &tcu, &tcv, &tcw);
+
+ ptcache_file_compressed_read(pf, (unsigned char*)dens, out_len_big);
+ ptcache_file_compressed_read(pf, (unsigned char*)tmp_array_big, out_len_big);
+
+ ptcache_file_compressed_read(pf, (unsigned char*)tcu, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tcv, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char*)tcw, out_len);
+
+ MEM_freeN(tmp_array_big);
+ }
+ }
+
+ return 1;
+}
+
+static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v)
+{
+ SmokeModifierData *smd= (SmokeModifierData *)smoke_v;
+ SmokeDomainSettings *sds = smd->domain;
+ char version[4];
+ int ch_res[3];
+ float ch_dx;
+ int fluid_fields = smoke_get_data_flags(sds);
+ int cache_fields = 0;
+ int active_fields = 0;
+ int reallocate = 0;
+
+ /* version header */
+ ptcache_file_read(pf, version, 4, sizeof(char));
+ if (!STREQLEN(version, SMOKE_CACHE_VERSION, 4))
+ {
+ /* reset file pointer */
+ fseek(pf->fp, -4, SEEK_CUR);
+ return ptcache_smoke_read_old(pf, smoke_v);
+ }
+
+ /* fluid info */
+ ptcache_file_read(pf, &cache_fields, 1, sizeof(int));
+ ptcache_file_read(pf, &active_fields, 1, sizeof(int));
+ ptcache_file_read(pf, &ch_res, 3, sizeof(int));
+ ptcache_file_read(pf, &ch_dx, 1, sizeof(float));
+
+ /* check if resolution has changed */
+ if (sds->res[0] != ch_res[0] ||
+ sds->res[1] != ch_res[1] ||
+ sds->res[2] != ch_res[2]) {
+ if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN)
+ reallocate = 1;
+ else
+ return 0;
+ }
+ /* check if active fields have changed */
+ if (fluid_fields != cache_fields ||
+ active_fields != sds->active_fields)
+ reallocate = 1;
+
+ /* reallocate fluid if needed*/
+ if (reallocate) {
+ sds->active_fields = active_fields | cache_fields;
+ smoke_reallocate_fluid(sds, ch_dx, ch_res, 1);
+ sds->dx = ch_dx;
+ VECCOPY(sds->res, ch_res);
+ sds->total_cells = ch_res[0]*ch_res[1]*ch_res[2];
+ if (sds->flags & MOD_SMOKE_HIGHRES) {
+ smoke_reallocate_highres_fluid(sds, ch_dx, ch_res, 1);
+ }
+ }
+
+ if (sds->fluid) {
+ size_t res = sds->res[0]*sds->res[1]*sds->res[2];
+ float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
+ unsigned char *obstacles;
+ unsigned int out_len = (unsigned int)res * sizeof(float);
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat, &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles);
+
+ ptcache_file_compressed_read(pf, (unsigned char *)sds->shadow, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)dens, out_len);
+ if (cache_fields & SM_ACTIVE_HEAT) {
+ ptcache_file_compressed_read(pf, (unsigned char *)heat, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)heatold, out_len);
+ }
+ if (cache_fields & SM_ACTIVE_FIRE) {
+ ptcache_file_compressed_read(pf, (unsigned char *)flame, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)fuel, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)react, out_len);
+ }
+ if (cache_fields & SM_ACTIVE_COLORS) {
+ ptcache_file_compressed_read(pf, (unsigned char *)r, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)g, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)b, out_len);
+ }
+ ptcache_file_compressed_read(pf, (unsigned char *)vx, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)vy, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)vz, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)obstacles, (unsigned int)res);
+ ptcache_file_read(pf, &dt, 1, sizeof(float));
+ ptcache_file_read(pf, &dx, 1, sizeof(float));
+ ptcache_file_read(pf, &sds->p0, 3, sizeof(float));
+ ptcache_file_read(pf, &sds->p1, 3, sizeof(float));
+ ptcache_file_read(pf, &sds->dp0, 3, sizeof(float));
+ ptcache_file_read(pf, &sds->shift, 3, sizeof(int));
+ ptcache_file_read(pf, &sds->obj_shift_f, 3, sizeof(float));
+ ptcache_file_read(pf, &sds->obmat, 16, sizeof(float));
+ ptcache_file_read(pf, &sds->base_res, 3, sizeof(int));
+ ptcache_file_read(pf, &sds->res_min, 3, sizeof(int));
+ ptcache_file_read(pf, &sds->res_max, 3, sizeof(int));
+ ptcache_file_read(pf, &sds->active_color, 3, sizeof(float));
+ }
+
+ if (pf->data_types & (1<<BPHYS_DATA_SMOKE_HIGH) && sds->wt) {
+ int res = sds->res[0]*sds->res[1]*sds->res[2];
+ int res_big, res_big_array[3];
+ float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
+ unsigned int out_len = sizeof(float)*(unsigned int)res;
+ unsigned int out_len_big;
+
+ smoke_turbulence_get_res(sds->wt, res_big_array);
+ res_big = res_big_array[0]*res_big_array[1]*res_big_array[2];
+ out_len_big = sizeof(float) * (unsigned int)res_big;
+
+ smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+
+ ptcache_file_compressed_read(pf, (unsigned char *)dens, out_len_big);
+ if (cache_fields & SM_ACTIVE_FIRE) {
+ ptcache_file_compressed_read(pf, (unsigned char *)flame, out_len_big);
+ ptcache_file_compressed_read(pf, (unsigned char *)fuel, out_len_big);
+ ptcache_file_compressed_read(pf, (unsigned char *)react, out_len_big);
+ }
+ if (cache_fields & SM_ACTIVE_COLORS) {
+ ptcache_file_compressed_read(pf, (unsigned char *)r, out_len_big);
+ ptcache_file_compressed_read(pf, (unsigned char *)g, out_len_big);
+ ptcache_file_compressed_read(pf, (unsigned char *)b, out_len_big);
+ }
+
+ ptcache_file_compressed_read(pf, (unsigned char *)tcu, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)tcv, out_len);
+ ptcache_file_compressed_read(pf, (unsigned char *)tcw, out_len);
+ }
+
+ return 1;
+}
+
+#ifdef WITH_OPENVDB
+/**
+ * Construct matrices which represent the fluid object, for low and high res:
+ * <pre>
+ * vs 0 0 0
+ * 0 vs 0 0
+ * 0 0 vs 0
+ * px py pz 1
+ * </pre>
+ *
+ * with `vs` = voxel size, and `px, py, pz`,
+ * the min position of the domain's bounding box.
+ */
+static void compute_fluid_matrices(SmokeDomainSettings *sds)
+{
+ float bbox_min[3];
+
+ copy_v3_v3(bbox_min, sds->p0);
+
+ if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) {
+ bbox_min[0] += (sds->cell_size[0] * (float)sds->res_min[0]);
+ bbox_min[1] += (sds->cell_size[1] * (float)sds->res_min[1]);
+ bbox_min[2] += (sds->cell_size[2] * (float)sds->res_min[2]);
+ add_v3_v3(bbox_min, sds->obj_shift_f);
+ }
+
+ /* construct low res matrix */
+ size_to_mat4(sds->fluidmat, sds->cell_size);
+ copy_v3_v3(sds->fluidmat[3], bbox_min);
+
+ /* The smoke simulator stores voxels cell-centered, whilst VDB is node
+ * centered, so we offset the matrix by half a voxel to compensate. */
+ madd_v3_v3fl(sds->fluidmat[3], sds->cell_size, 0.5f);
+
+ mul_m4_m4m4(sds->fluidmat, sds->obmat, sds->fluidmat);
+
+ if (sds->wt) {
+ float voxel_size_high[3];
+ /* construct high res matrix */
+ mul_v3_v3fl(voxel_size_high, sds->cell_size, 1.0f / (float)(sds->amplify + 1));
+ size_to_mat4(sds->fluidmat_wt, voxel_size_high);
+ copy_v3_v3(sds->fluidmat_wt[3], bbox_min);
+
+ /* Same here, add half a voxel to adjust the position of the fluid. */
+ madd_v3_v3fl(sds->fluidmat_wt[3], voxel_size_high, 0.5f);
+
+ mul_m4_m4m4(sds->fluidmat_wt, sds->obmat, sds->fluidmat_wt);
+ }
+}
+
+static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke_v)
+{
+ SmokeModifierData *smd = (SmokeModifierData *)smoke_v;
+ SmokeDomainSettings *sds = smd->domain;
+
+ OpenVDBWriter_set_flags(writer, sds->openvdb_comp, (sds->data_depth == 16));
+
+ OpenVDBWriter_add_meta_int(writer, "blender/smoke/active_fields", sds->active_fields);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/resolution", sds->res);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/min_resolution", sds->res_min);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/max_resolution", sds->res_max);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/base_resolution", sds->base_res);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/min_bbox", sds->p0);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/max_bbox", sds->p1);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/dp0", sds->dp0);
+ OpenVDBWriter_add_meta_v3_int(writer, "blender/smoke/shift", sds->shift);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/obj_shift_f", sds->obj_shift_f);
+ OpenVDBWriter_add_meta_v3(writer, "blender/smoke/active_color", sds->active_color);
+ OpenVDBWriter_add_meta_mat4(writer, "blender/smoke/obmat", sds->obmat);
+
+ int fluid_fields = smoke_get_data_flags(sds);
+
+ struct OpenVDBFloatGrid *clip_grid = NULL;
+
+ compute_fluid_matrices(sds);
+
+ OpenVDBWriter_add_meta_int(writer, "blender/smoke/fluid_fields", fluid_fields);
+
+ if (sds->wt) {
+ struct OpenVDBFloatGrid *wt_density_grid;
+ float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
+
+ smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+
+ wt_density_grid = OpenVDB_export_grid_fl(writer, "density", dens, sds->res_wt, sds->fluidmat_wt, NULL);
+ clip_grid = wt_density_grid;
+
+ if (fluid_fields & SM_ACTIVE_FIRE) {
+ OpenVDB_export_grid_fl(writer, "flame", flame, sds->res_wt, sds->fluidmat_wt, wt_density_grid);
+ OpenVDB_export_grid_fl(writer, "fuel", fuel, sds->res_wt, sds->fluidmat_wt, wt_density_grid);
+ OpenVDB_export_grid_fl(writer, "react", react, sds->res_wt, sds->fluidmat_wt, wt_density_grid);
+ }
+
+ if (fluid_fields & SM_ACTIVE_COLORS) {
+ OpenVDB_export_grid_vec(writer, "color", r, g, b, sds->res_wt, sds->fluidmat_wt, VEC_INVARIANT, true, wt_density_grid);
+ }
+
+ OpenVDB_export_grid_vec(writer, "texture coordinates", tcu, tcv, tcw, sds->res, sds->fluidmat, VEC_INVARIANT, false, wt_density_grid);
+ }
+
+ if (sds->fluid) {
+ struct OpenVDBFloatGrid *density_grid;
+ float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
+ unsigned char *obstacles;
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat,
+ &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles);
+
+ OpenVDBWriter_add_meta_fl(writer, "blender/smoke/dx", dx);
+ OpenVDBWriter_add_meta_fl(writer, "blender/smoke/dt", dt);
+
+ const char *name = (!sds->wt) ? "density" : "density low";
+ density_grid = OpenVDB_export_grid_fl(writer, name, dens, sds->res, sds->fluidmat, NULL);
+ clip_grid = sds->wt ? clip_grid : density_grid;
+
+ OpenVDB_export_grid_fl(writer, "shadow", sds->shadow, sds->res, sds->fluidmat, NULL);
+
+ if (fluid_fields & SM_ACTIVE_HEAT) {
+ OpenVDB_export_grid_fl(writer, "heat", heat, sds->res, sds->fluidmat, clip_grid);
+ OpenVDB_export_grid_fl(writer, "heat old", heatold, sds->res, sds->fluidmat, clip_grid);
+ }
+
+ if (fluid_fields & SM_ACTIVE_FIRE) {
+ name = (!sds->wt) ? "flame" : "flame low";
+ OpenVDB_export_grid_fl(writer, name, flame, sds->res, sds->fluidmat, density_grid);
+ name = (!sds->wt) ? "fuel" : "fuel low";
+ OpenVDB_export_grid_fl(writer, name, fuel, sds->res, sds->fluidmat, density_grid);
+ name = (!sds->wt) ? "react" : "react low";
+ OpenVDB_export_grid_fl(writer, name, react, sds->res, sds->fluidmat, density_grid);
+ }
+
+ if (fluid_fields & SM_ACTIVE_COLORS) {
+ name = (!sds->wt) ? "color" : "color low";
+ OpenVDB_export_grid_vec(writer, name, r, g, b, sds->res, sds->fluidmat, VEC_INVARIANT, true, density_grid);
+ }
+
+ OpenVDB_export_grid_vec(writer, "velocity", vx, vy, vz, sds->res, sds->fluidmat, VEC_CONTRAVARIANT_RELATIVE, false, clip_grid);
+ OpenVDB_export_grid_ch(writer, "obstacles", obstacles, sds->res, sds->fluidmat, NULL);
+ }
+
+ return 1;
+}
+
+static int ptcache_smoke_openvdb_read(struct OpenVDBReader *reader, void *smoke_v)
+{
+ SmokeModifierData *smd = (SmokeModifierData *)smoke_v;
+
+ if (!smd) {
+ return 0;
+ }
+
+ SmokeDomainSettings *sds = smd->domain;
+
+ int fluid_fields = smoke_get_data_flags(sds);
+ int active_fields, cache_fields = 0;
+ int cache_res[3];
+ float cache_dx;
+ bool reallocate = false;
+
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/min_resolution", sds->res_min);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/max_resolution", sds->res_max);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/base_resolution", sds->base_res);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/min_bbox", sds->p0);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/max_bbox", sds->p1);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/dp0", sds->dp0);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/shift", sds->shift);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/obj_shift_f", sds->obj_shift_f);
+ OpenVDBReader_get_meta_v3(reader, "blender/smoke/active_color", sds->active_color);
+ OpenVDBReader_get_meta_mat4(reader, "blender/smoke/obmat", sds->obmat);
+ OpenVDBReader_get_meta_int(reader, "blender/smoke/fluid_fields", &cache_fields);
+ OpenVDBReader_get_meta_int(reader, "blender/smoke/active_fields", &active_fields);
+ OpenVDBReader_get_meta_fl(reader, "blender/smoke/dx", &cache_dx);
+ OpenVDBReader_get_meta_v3_int(reader, "blender/smoke/resolution", cache_res);
+
+ /* check if resolution has changed */
+ if (sds->res[0] != cache_res[0] ||
+ sds->res[1] != cache_res[1] ||
+ sds->res[2] != cache_res[2])
+ {
+ if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) {
+ reallocate = true;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ /* check if active fields have changed */
+ if ((fluid_fields != cache_fields) || (active_fields != sds->active_fields)) {
+ reallocate = true;
+ }
+
+ /* reallocate fluid if needed*/
+ if (reallocate) {
+ sds->active_fields = active_fields | cache_fields;
+ smoke_reallocate_fluid(sds, cache_dx, cache_res, 1);
+ sds->dx = cache_dx;
+ copy_v3_v3_int(sds->res, cache_res);
+ sds->total_cells = cache_res[0] * cache_res[1] * cache_res[2];
+
+ if (sds->flags & MOD_SMOKE_HIGHRES) {
+ smoke_reallocate_highres_fluid(sds, cache_dx, cache_res, 1);
+ }
+ }
+
+ if (sds->fluid) {
+ float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
+ unsigned char *obstacles;
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat,
+ &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles);
+
+ OpenVDBReader_get_meta_fl(reader, "blender/smoke/dt", &dt);
+
+ OpenVDB_import_grid_fl(reader, "shadow", &sds->shadow, sds->res);
+
+ const char *name = (!sds->wt) ? "density" : "density low";
+ OpenVDB_import_grid_fl(reader, name, &dens, sds->res);
+
+ if (cache_fields & SM_ACTIVE_HEAT) {
+ OpenVDB_import_grid_fl(reader, "heat", &heat, sds->res);
+ OpenVDB_import_grid_fl(reader, "heat old", &heatold, sds->res);
+ }
+
+ if (cache_fields & SM_ACTIVE_FIRE) {
+ name = (!sds->wt) ? "flame" : "flame low";
+ OpenVDB_import_grid_fl(reader, name, &flame, sds->res);
+ name = (!sds->wt) ? "fuel" : "fuel low";
+ OpenVDB_import_grid_fl(reader, name, &fuel, sds->res);
+ name = (!sds->wt) ? "react" : "react low";
+ OpenVDB_import_grid_fl(reader, name, &react, sds->res);
+ }
+
+ if (cache_fields & SM_ACTIVE_COLORS) {
+ name = (!sds->wt) ? "color" : "color low";
+ OpenVDB_import_grid_vec(reader, name, &r, &g, &b, sds->res);
+ }
+
+ OpenVDB_import_grid_vec(reader, "velocity", &vx, &vy, &vz, sds->res);
+ OpenVDB_import_grid_ch(reader, "obstacles", &obstacles, sds->res);
+ }
+
+ if (sds->wt) {
+ float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
+
+ smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+
+ OpenVDB_import_grid_fl(reader, "density", &dens, sds->res_wt);
+
+ if (cache_fields & SM_ACTIVE_FIRE) {
+ OpenVDB_import_grid_fl(reader, "flame", &flame, sds->res_wt);
+ OpenVDB_import_grid_fl(reader, "fuel", &fuel, sds->res_wt);
+ OpenVDB_import_grid_fl(reader, "react", &react, sds->res_wt);
+ }
+
+ if (cache_fields & SM_ACTIVE_COLORS) {
+ OpenVDB_import_grid_vec(reader, "color", &r, &g, &b, sds->res_wt);
+ }
+
+ OpenVDB_import_grid_vec(reader, "texture coordinates", &tcu, &tcv, &tcw, sds->res);
+ }
+
+ OpenVDBReader_free(reader);
+
+ return 1;
+}
+#endif
+
+#else // WITH_SMOKE
+static int ptcache_smoke_totpoint(void *UNUSED(smoke_v), int UNUSED(cfra)) { return 0; }
+static void ptcache_smoke_error(void *UNUSED(smoke_v), const char *UNUSED(message)) { }
+static int ptcache_smoke_read(PTCacheFile *UNUSED(pf), void *UNUSED(smoke_v)) { return 0; }
+static int ptcache_smoke_write(PTCacheFile *UNUSED(pf), void *UNUSED(smoke_v)) { return 0; }
+#endif // WITH_SMOKE
+
+#if !defined(WITH_SMOKE) || !defined(WITH_OPENVDB)
+static int ptcache_smoke_openvdb_write(struct OpenVDBWriter *writer, void *smoke_v)
+{
+ UNUSED_VARS(writer, smoke_v);
+ return 0;
+}
+
+static int ptcache_smoke_openvdb_read(struct OpenVDBReader *reader, void *smoke_v)
+{
+ UNUSED_VARS(reader, smoke_v);
+ return 0;
+}
+#endif
+
+static int ptcache_dynamicpaint_totpoint(void *sd, int UNUSED(cfra))
+{
+ DynamicPaintSurface *surface = (DynamicPaintSurface*)sd;
+
+ if (!surface->data) return 0;
+ else return surface->data->total_points;
+}
+
+static void ptcache_dynamicpaint_error(void *UNUSED(sd), const char *UNUSED(message))
+{
+ /* ignored for now */
+}
+
+#define DPAINT_CACHE_VERSION "1.01"
+
+static int ptcache_dynamicpaint_write(PTCacheFile *pf, void *dp_v)
+{
+ DynamicPaintSurface *surface = (DynamicPaintSurface*)dp_v;
+ int cache_compress = 1;
+
+ /* version header */
+ ptcache_file_write(pf, DPAINT_CACHE_VERSION, 1, sizeof(char) * 4);
+
+ if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->data) {
+ int total_points=surface->data->total_points;
+ unsigned int in_len;
+ unsigned char *out;
+
+ /* cache type */
+ ptcache_file_write(pf, &surface->type, 1, sizeof(int));
+
+ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
+ in_len = sizeof(PaintPoint) * total_points;
+ }
+ else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE ||
+ surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)
+ {
+ in_len = sizeof(float) * total_points;
+ }
+ else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
+ in_len = sizeof(PaintWavePoint) * total_points;
+ }
+ else {
+ return 0;
+ }
+
+ out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len), "pointcache_lzo_buffer");
+
+ ptcache_file_compressed_write(pf, (unsigned char *)surface->data->type_data, in_len, out, cache_compress);
+ MEM_freeN(out);
+
+ }
+ return 1;
+}
+static int ptcache_dynamicpaint_read(PTCacheFile *pf, void *dp_v)
+{
+ DynamicPaintSurface *surface = (DynamicPaintSurface*)dp_v;
+ char version[4];
+
+ /* version header */
+ ptcache_file_read(pf, version, 1, sizeof(char) * 4);
+ if (!STREQLEN(version, DPAINT_CACHE_VERSION, 4)) {
+ printf("Dynamic Paint: Invalid cache version: '%c%c%c%c'!\n", UNPACK4(version));
+ return 0;
+ }
+
+ if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->data) {
+ unsigned int data_len;
+ int surface_type;
+
+ /* cache type */
+ ptcache_file_read(pf, &surface_type, 1, sizeof(int));
+
+ if (surface_type != surface->type)
+ return 0;
+
+ /* read surface data */
+ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
+ data_len = sizeof(PaintPoint);
+ }
+ else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE ||
+ surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)
+ {
+ data_len = sizeof(float);
+ }
+ else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
+ data_len = sizeof(PaintWavePoint);
+ }
+ else {
+ return 0;
+ }
+
+ ptcache_file_compressed_read(pf, (unsigned char *)surface->data->type_data, data_len*surface->data->total_points);
+
+ }
+ return 1;
+}
+
+/* Rigid Body functions */
+static int ptcache_rigidbody_write(int index, void *rb_v, void **data, int UNUSED(cfra))
+{
+ RigidBodyWorld *rbw = rb_v;
+ Object *ob = NULL;
+
+ if (rbw->objects)
+ ob = rbw->objects[index];
+
+ if (ob && ob->rigidbody_object) {
+ RigidBodyOb *rbo = ob->rigidbody_object;
+
+ if (rbo->type == RBO_TYPE_ACTIVE) {
+#ifdef WITH_BULLET
+ RB_body_get_position(rbo->physics_object, rbo->pos);
+ RB_body_get_orientation(rbo->physics_object, rbo->orn);
+#endif
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_LOCATION, rbo->pos);
+ PTCACHE_DATA_FROM(data, BPHYS_DATA_ROTATION, rbo->orn);
+ }
+ }
+
+ return 1;
+}
+static void ptcache_rigidbody_read(int index, void *rb_v, void **data, float UNUSED(cfra), float *old_data)
+{
+ RigidBodyWorld *rbw = rb_v;
+ Object *ob = NULL;
+
+ if (rbw->objects)
+ ob = rbw->objects[index];
+
+ if (ob && ob->rigidbody_object) {
+ RigidBodyOb *rbo = ob->rigidbody_object;
+
+ if (rbo->type == RBO_TYPE_ACTIVE) {
+
+ if (old_data) {
+ memcpy(rbo->pos, data, 3 * sizeof(float));
+ memcpy(rbo->orn, data + 3, 4 * sizeof(float));
+ }
+ else {
+ PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, rbo->pos);
+ PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, 0, rbo->orn);
+ }
+ }
+ }
+}
+static void ptcache_rigidbody_interpolate(int index, void *rb_v, void **data, float cfra, float cfra1, float cfra2, float *old_data)
+{
+ RigidBodyWorld *rbw = rb_v;
+ Object *ob = NULL;
+
+ if (rbw->objects)
+ ob = rbw->objects[index];
+
+ if (ob && ob->rigidbody_object) {
+ RigidBodyOb *rbo = ob->rigidbody_object;
+
+ if (rbo->type == RBO_TYPE_ACTIVE) {
+ ParticleKey keys[4];
+ ParticleKey result;
+ float dfra;
+
+ memset(keys, 0, sizeof(keys));
+
+ copy_v3_v3(keys[1].co, rbo->pos);
+ copy_qt_qt(keys[1].rot, rbo->orn);
+
+ if (old_data) {
+ memcpy(keys[2].co, data, 3 * sizeof(float));
+ memcpy(keys[2].rot, data + 3, 4 * sizeof(float));
+ }
+ else {
+ BKE_ptcache_make_particle_key(&keys[2], 0, data, cfra2);
+ }
+
+ dfra = cfra2 - cfra1;
+
+ /* note: keys[0] and keys[3] unused for type < 1 (crappy) */
+ psys_interpolate_particle(-1, keys, (cfra - cfra1) / dfra, &result, true);
+ interp_qt_qtqt(result.rot, keys[1].rot, keys[2].rot, (cfra - cfra1) / dfra);
+
+ copy_v3_v3(rbo->pos, result.co);
+ copy_qt_qt(rbo->orn, result.rot);
+ }
+ }
+}
+static int ptcache_rigidbody_totpoint(void *rb_v, int UNUSED(cfra))
+{
+ RigidBodyWorld *rbw = rb_v;
+
+ return rbw->numbodies;
+}
+
+static void ptcache_rigidbody_error(void *UNUSED(rb_v), const char *UNUSED(message))
+{
+ /* ignored for now */
+}
+
+/* Creating ID's */
+void BKE_ptcache_id_from_softbody(PTCacheID *pid, Object *ob, SoftBody *sb)
+{
+ memset(pid, 0, sizeof(PTCacheID));
+
+ pid->ob= ob;
+ pid->calldata= sb;
+ pid->type= PTCACHE_TYPE_SOFTBODY;
+ pid->cache= sb->pointcache;
+ pid->cache_ptr= &sb->pointcache;
+ pid->ptcaches= &sb->ptcaches;
+ pid->totpoint= pid->totwrite= ptcache_softbody_totpoint;
+ pid->error = ptcache_softbody_error;
+
+ pid->write_point = ptcache_softbody_write;
+ pid->read_point = ptcache_softbody_read;
+ pid->interpolate_point = ptcache_softbody_interpolate;
+
+ pid->write_stream = NULL;
+ pid->read_stream = NULL;
+
+ pid->write_openvdb_stream = NULL;
+ pid->read_openvdb_stream = NULL;
+
+ pid->write_extra_data = NULL;
+ pid->read_extra_data = NULL;
+ pid->interpolate_extra_data = NULL;
+
+ pid->write_header = ptcache_basic_header_write;
+ pid->read_header = ptcache_basic_header_read;
+
+ pid->data_types= (1<<BPHYS_DATA_LOCATION) | (1<<BPHYS_DATA_VELOCITY);
+ pid->info_types= 0;
+
+ pid->stack_index = pid->cache->index;
+
+ pid->default_step = 10;
+ pid->max_step = 20;
+ pid->file_type = PTCACHE_FILE_PTCACHE;
+}
+void BKE_ptcache_id_from_particles(PTCacheID *pid, Object *ob, ParticleSystem *psys)
+{
+ memset(pid, 0, sizeof(PTCacheID));
+
+ pid->ob= ob;
+ pid->calldata= psys;
+ pid->type= PTCACHE_TYPE_PARTICLES;
+ pid->stack_index= psys->pointcache->index;
+ pid->cache= psys->pointcache;
+ pid->cache_ptr= &psys->pointcache;
+ pid->ptcaches= &psys->ptcaches;
+
+ if (psys->part->type != PART_HAIR)
+ pid->flag |= PTCACHE_VEL_PER_SEC;
+
+ pid->totpoint = ptcache_particle_totpoint;
+ pid->totwrite = ptcache_particle_totwrite;
+ pid->error = ptcache_particle_error;
+
+ pid->write_point = ptcache_particle_write;
+ pid->read_point = ptcache_particle_read;
+ pid->interpolate_point = ptcache_particle_interpolate;
+
+ pid->write_stream = NULL;
+ pid->read_stream = NULL;
+
+ pid->write_openvdb_stream = NULL;
+ pid->read_openvdb_stream = NULL;
+
+ pid->write_extra_data = NULL;
+ pid->read_extra_data = NULL;
+ pid->interpolate_extra_data = NULL;
+
+ pid->write_header = ptcache_basic_header_write;
+ pid->read_header = ptcache_basic_header_read;
+
+ pid->data_types = (1<<BPHYS_DATA_LOCATION) | (1<<BPHYS_DATA_VELOCITY) | (1<<BPHYS_DATA_INDEX);
+
+ if (psys->part->phystype == PART_PHYS_BOIDS)
+ pid->data_types|= (1<<BPHYS_DATA_AVELOCITY) | (1<<BPHYS_DATA_ROTATION) | (1<<BPHYS_DATA_BOIDS);
+ else if (psys->part->phystype == PART_PHYS_FLUID && psys->part->fluid && psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS) {
+ pid->write_extra_data = ptcache_particle_extra_write;
+ pid->read_extra_data = ptcache_particle_extra_read;
+ }
+
+ if (psys->part->flag & PART_ROTATIONS) {
+ pid->data_types|= (1<<BPHYS_DATA_ROTATION);
+
+ if (psys->part->rotmode != PART_ROT_VEL ||
+ psys->part->avemode == PART_AVE_RAND ||
+ psys->part->avefac != 0.0f)
+ {
+ pid->data_types |= (1 << BPHYS_DATA_AVELOCITY);
+ }
+ }
+
+ pid->info_types= (1<<BPHYS_DATA_TIMES);
+
+ pid->default_step = 10;
+ pid->max_step = 20;
+ pid->file_type = PTCACHE_FILE_PTCACHE;
+}
+void BKE_ptcache_id_from_cloth(PTCacheID *pid, Object *ob, ClothModifierData *clmd)
+{
+ memset(pid, 0, sizeof(PTCacheID));
+
+ pid->ob= ob;
+ pid->calldata= clmd;
+ pid->type= PTCACHE_TYPE_CLOTH;
+ pid->stack_index= clmd->point_cache->index;
+ pid->cache= clmd->point_cache;
+ pid->cache_ptr= &clmd->point_cache;
+ pid->ptcaches= &clmd->ptcaches;
+ pid->totpoint= pid->totwrite= ptcache_cloth_totpoint;
+ pid->error = ptcache_cloth_error;
+
+ pid->write_point = ptcache_cloth_write;
+ pid->read_point = ptcache_cloth_read;
+ pid->interpolate_point = ptcache_cloth_interpolate;
+
+ pid->write_openvdb_stream = NULL;
+ pid->read_openvdb_stream = NULL;
+
+ pid->write_stream = NULL;
+ pid->read_stream = NULL;
+
+ pid->write_extra_data = NULL;
+ pid->read_extra_data = NULL;
+ pid->interpolate_extra_data = NULL;
+
+ pid->write_header = ptcache_basic_header_write;
+ pid->read_header = ptcache_basic_header_read;
+
+ pid->data_types= (1<<BPHYS_DATA_LOCATION) | (1<<BPHYS_DATA_VELOCITY) | (1<<BPHYS_DATA_XCONST);
+ pid->info_types= 0;
+
+ pid->default_step = 1;
+ pid->max_step = 1;
+ pid->file_type = PTCACHE_FILE_PTCACHE;
+}
+void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct SmokeModifierData *smd)
+{
+ SmokeDomainSettings *sds = smd->domain;
+
+ memset(pid, 0, sizeof(PTCacheID));
+
+ pid->ob= ob;
+ pid->calldata= smd;
+
+ pid->type= PTCACHE_TYPE_SMOKE_DOMAIN;
+ pid->stack_index= sds->point_cache[0]->index;
+
+ pid->cache= sds->point_cache[0];
+ pid->cache_ptr= &(sds->point_cache[0]);
+ pid->ptcaches= &(sds->ptcaches[0]);
+
+ pid->totpoint= pid->totwrite= ptcache_smoke_totpoint;
+ pid->error = ptcache_smoke_error;
+
+ pid->write_point = NULL;
+ pid->read_point = NULL;
+ pid->interpolate_point = NULL;
+
+ pid->read_stream = ptcache_smoke_read;
+ pid->write_stream = ptcache_smoke_write;
+
+ pid->write_openvdb_stream = ptcache_smoke_openvdb_write;
+ pid->read_openvdb_stream = ptcache_smoke_openvdb_read;
+
+ pid->write_extra_data = NULL;
+ pid->read_extra_data = NULL;
+ pid->interpolate_extra_data = NULL;
+
+ pid->write_header = ptcache_basic_header_write;
+ pid->read_header = ptcache_basic_header_read;
+
+ pid->data_types= 0;
+ pid->info_types= 0;
+
+ if (sds->fluid)
+ pid->data_types |= (1<<BPHYS_DATA_SMOKE_LOW);
+ if (sds->wt)
+ pid->data_types |= (1<<BPHYS_DATA_SMOKE_HIGH);
+
+ pid->default_step = 1;
+ pid->max_step = 1;
+ pid->file_type = smd->domain->cache_file_format;
+}
+
+void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, Object *ob, DynamicPaintSurface *surface)
+{
+
+ memset(pid, 0, sizeof(PTCacheID));
+
+ pid->ob= ob;
+ pid->calldata= surface;
+ pid->type= PTCACHE_TYPE_DYNAMICPAINT;
+ pid->cache= surface->pointcache;
+ pid->cache_ptr= &surface->pointcache;
+ pid->ptcaches= &surface->ptcaches;
+ pid->totpoint= pid->totwrite= ptcache_dynamicpaint_totpoint;
+ pid->error = ptcache_dynamicpaint_error;
+
+ pid->write_point = NULL;
+ pid->read_point = NULL;
+ pid->interpolate_point = NULL;
+
+ pid->write_stream = ptcache_dynamicpaint_write;
+ pid->read_stream = ptcache_dynamicpaint_read;
+
+ pid->write_openvdb_stream = NULL;
+ pid->read_openvdb_stream = NULL;
+
+ pid->write_extra_data = NULL;
+ pid->read_extra_data = NULL;
+ pid->interpolate_extra_data = NULL;
+
+ pid->write_header = ptcache_basic_header_write;
+ pid->read_header = ptcache_basic_header_read;
+
+ pid->data_types= BPHYS_DATA_DYNAMICPAINT;
+ pid->info_types= 0;
+
+ pid->stack_index = pid->cache->index;
+
+ pid->default_step = 1;
+ pid->max_step = 1;
+ pid->file_type = PTCACHE_FILE_PTCACHE;
+}
+
+void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, Object *ob, RigidBodyWorld *rbw)
+{
+
+ memset(pid, 0, sizeof(PTCacheID));
+
+ pid->ob= ob;
+ pid->calldata= rbw;
+ pid->type= PTCACHE_TYPE_RIGIDBODY;
+ pid->cache= rbw->pointcache;
+ pid->cache_ptr= &rbw->pointcache;
+ pid->ptcaches= &rbw->ptcaches;
+ pid->totpoint= pid->totwrite= ptcache_rigidbody_totpoint;
+ pid->error = ptcache_rigidbody_error;
+
+ pid->write_point = ptcache_rigidbody_write;
+ pid->read_point = ptcache_rigidbody_read;
+ pid->interpolate_point = ptcache_rigidbody_interpolate;
+
+ pid->write_stream = NULL;
+ pid->read_stream = NULL;
+
+ pid->write_openvdb_stream = NULL;
+ pid->read_openvdb_stream = NULL;
+
+ pid->write_extra_data = NULL;
+ pid->read_extra_data = NULL;
+ pid->interpolate_extra_data = NULL;
+
+ pid->write_header = ptcache_basic_header_write;
+ pid->read_header = ptcache_basic_header_read;
+
+ pid->data_types= (1<<BPHYS_DATA_LOCATION) | (1<<BPHYS_DATA_ROTATION);
+ pid->info_types= 0;
+
+ pid->stack_index = pid->cache->index;
+
+ pid->default_step = 1;
+ pid->max_step = 1;
+ pid->file_type = PTCACHE_FILE_PTCACHE;
+}
+
+void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob, Scene *scene, int duplis)
+{
+ PTCacheID *pid;
+ ParticleSystem *psys;
+ ModifierData *md;
+
+ lb->first= lb->last= NULL;
+
+ if (ob->soft) {
+ pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+ BKE_ptcache_id_from_softbody(pid, ob, ob->soft);
+ BLI_addtail(lb, pid);
+ }
+
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ if (psys->part==NULL)
+ continue;
+
+ /* check to make sure point cache is actually used by the particles */
+ if (ELEM(psys->part->phystype, PART_PHYS_NO, PART_PHYS_KEYED))
+ continue;
+
+ /* hair needs to be included in id-list for cache edit mode to work */
+ /* if (psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DYNAMICS)==0) */
+ /* continue; */
+
+ if (psys->part->type == PART_FLUID)
+ continue;
+
+ pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+ BKE_ptcache_id_from_particles(pid, ob, psys);
+ BLI_addtail(lb, pid);
+ }
+
+ for (md=ob->modifiers.first; md; md=md->next) {
+ if (md->type == eModifierType_Cloth) {
+ pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+ BKE_ptcache_id_from_cloth(pid, ob, (ClothModifierData*)md);
+ BLI_addtail(lb, pid);
+ }
+ else if (md->type == eModifierType_Smoke) {
+ SmokeModifierData *smd = (SmokeModifierData *)md;
+ if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
+ pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+ BKE_ptcache_id_from_smoke(pid, ob, (SmokeModifierData*)md);
+ BLI_addtail(lb, pid);
+ }
+ }
+ else if (md->type == eModifierType_DynamicPaint) {
+ DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
+ if (pmd->canvas) {
+ DynamicPaintSurface *surface = pmd->canvas->surfaces.first;
+
+ for (; surface; surface=surface->next) {
+ pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+ BKE_ptcache_id_from_dynamicpaint(pid, ob, surface);
+ BLI_addtail(lb, pid);
+ }
+ }
+ }
+ }
+
+ if (scene && ob->rigidbody_object && scene->rigidbody_world) {
+ pid = MEM_callocN(sizeof(PTCacheID), "PTCacheID");
+ BKE_ptcache_id_from_rigidbody(pid, ob, scene->rigidbody_world);
+ BLI_addtail(lb, pid);
+ }
+
+ if (scene && (duplis-- > 0) && (ob->transflag & OB_DUPLI)) {
+ ListBase *lb_dupli_ob;
+ /* don't update the dupli groups, we only want their pid's */
+ if ((lb_dupli_ob = object_duplilist_ex(G.main->eval_ctx, scene, ob, false))) {
+ DupliObject *dob;
+ for (dob= lb_dupli_ob->first; dob; dob= dob->next) {
+ if (dob->ob != ob) { /* avoids recursive loops with dupliframes: bug 22988 */
+ ListBase lb_dupli_pid;
+ BKE_ptcache_ids_from_object(&lb_dupli_pid, dob->ob, scene, duplis);
+ BLI_movelisttolist(lb, &lb_dupli_pid);
+ if (lb_dupli_pid.first)
+ printf("Adding Dupli\n");
+ }
+ }
+
+ free_object_duplilist(lb_dupli_ob); /* does restore */
+ }
+ }
+}
+
+/* File handling */
+
+static const char *ptcache_file_extension(const PTCacheID *pid)
+{
+ switch (pid->file_type) {
+ default:
+ case PTCACHE_FILE_PTCACHE:
+ return PTCACHE_EXT;
+ case PTCACHE_FILE_OPENVDB:
+ return ".vdb";
+ }
+}
+
+/**
+ * Similar to #BLI_path_frame_get, but takes into account the stack-index which is after the frame.
+ */
+static int ptcache_frame_from_filename(const char *filename, const char *ext)
+{
+ const int frame_len = 6;
+ const int ext_len = frame_len + strlen(ext);
+ const int len = strlen(filename);
+
+ /* could crash if trying to copy a string out of this range */
+ if (len > ext_len) {
+ /* using frame_len here gives compile error (vla) */
+ char num[/* frame_len */6 + 1];
+ BLI_strncpy(num, filename + len - ext_len, sizeof(num));
+
+ return atoi(num);
+ }
+
+ return -1;
+}
+
+/* Takes an Object ID and returns a unique name
+ * - id: object id
+ * - cfra: frame for the cache, can be negative
+ * - stack_index: index in the modifier stack. we can have cache for more than one stack_index
+ */
+
+#define MAX_PTCACHE_PATH FILE_MAX
+#define MAX_PTCACHE_FILE (FILE_MAX * 2)
+
+static int ptcache_path(PTCacheID *pid, char *filename)
+{
+ Library *lib = (pid->ob) ? pid->ob->id.lib : NULL;
+ const char *blendfilename= (lib && (pid->cache->flag & PTCACHE_IGNORE_LIBPATH)==0) ? lib->filepath: G.main->name;
+ size_t i;
+
+ if (pid->cache->flag & PTCACHE_EXTERNAL) {
+ strcpy(filename, pid->cache->path);
+
+ if (BLI_path_is_rel(filename)) {
+ BLI_path_abs(filename, blendfilename);
+ }
+
+ return BLI_add_slash(filename); /* new strlen() */
+ }
+ else if (G.relbase_valid || lib) {
+ char file[MAX_PTCACHE_PATH]; /* we don't want the dir, only the file */
+
+ BLI_split_file_part(blendfilename, file, sizeof(file));
+ i = strlen(file);
+
+ /* remove .blend */
+ if (i > 6)
+ file[i-6] = '\0';
+
+ BLI_snprintf(filename, MAX_PTCACHE_PATH, "//"PTCACHE_PATH"%s", file); /* add blend file name to pointcache dir */
+ BLI_path_abs(filename, blendfilename);
+ return BLI_add_slash(filename); /* new strlen() */
+ }
+
+ /* use the temp path. this is weak but better then not using point cache at all */
+ /* temporary directory is assumed to exist and ALWAYS has a trailing slash */
+ BLI_snprintf(filename, MAX_PTCACHE_PATH, "%s"PTCACHE_PATH, BKE_tempdir_session());
+
+ return BLI_add_slash(filename); /* new strlen() */
+}
+
+static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_path, short do_ext)
+{
+ int len=0;
+ char *idname;
+ char *newname;
+ filename[0] = '\0';
+ newname = filename;
+
+ if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL)==0) return 0; /* save blend file before using disk pointcache */
+
+ /* start with temp dir */
+ if (do_path) {
+ len = ptcache_path(pid, filename);
+ newname += len;
+ }
+ if (pid->cache->name[0] == '\0' && (pid->cache->flag & PTCACHE_EXTERNAL)==0) {
+ idname = (pid->ob->id.name + 2);
+ /* convert chars to hex so they are always a valid filename */
+ while ('\0' != *idname) {
+ BLI_snprintf(newname, MAX_PTCACHE_FILE, "%02X", (unsigned int)(*idname++));
+ newname+=2;
+ len += 2;
+ }
+ }
+ else {
+ int temp = (int)strlen(pid->cache->name);
+ strcpy(newname, pid->cache->name);
+ newname+=temp;
+ len += temp;
+ }
+
+ if (do_ext) {
+ if (pid->cache->index < 0)
+ pid->cache->index = pid->stack_index = BKE_object_insert_ptcache(pid->ob);
+
+ const char *ext = ptcache_file_extension(pid);
+
+ if (pid->cache->flag & PTCACHE_EXTERNAL) {
+ if (pid->cache->index >= 0)
+ BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02u%s", cfra, pid->stack_index, ext); /* always 6 chars */
+ else
+ BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d%s", cfra, ext); /* always 6 chars */
+ }
+ else {
+ BLI_snprintf(newname, MAX_PTCACHE_FILE, "_%06d_%02u%s", cfra, pid->stack_index, ext); /* always 6 chars */
+ }
+ len += 16;
+ }
+
+ return len; /* make sure the above string is always 16 chars */
+}
+
+/* youll need to close yourself after! */
+static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra)
+{
+ PTCacheFile *pf;
+ FILE *fp = NULL;
+ char filename[FILE_MAX * 2];
+
+#ifndef DURIAN_POINTCACHE_LIB_OK
+ /* don't allow writing for linked objects */
+ if (pid->ob->id.lib && mode == PTCACHE_FILE_WRITE)
+ return NULL;
+#endif
+ if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL)==0) return NULL; /* save blend file before using disk pointcache */
+
+ ptcache_filename(pid, filename, cfra, 1, 1);
+
+ if (mode==PTCACHE_FILE_READ) {
+ fp = BLI_fopen(filename, "rb");
+ }
+ else if (mode==PTCACHE_FILE_WRITE) {
+ BLI_make_existing_file(filename); /* will create the dir if needs be, same as //textures is created */
+ fp = BLI_fopen(filename, "wb");
+ }
+ else if (mode==PTCACHE_FILE_UPDATE) {
+ BLI_make_existing_file(filename);
+ fp = BLI_fopen(filename, "rb+");
+ }
+
+ if (!fp)
+ return NULL;
+
+ pf= MEM_mallocN(sizeof(PTCacheFile), "PTCacheFile");
+ pf->fp= fp;
+ pf->old_format = 0;
+ pf->frame = cfra;
+
+ return pf;
+}
+static void ptcache_file_close(PTCacheFile *pf)
+{
+ if (pf) {
+ fclose(pf->fp);
+ MEM_freeN(pf);
+ }
+}
+
+static int ptcache_file_compressed_read(PTCacheFile *pf, unsigned char *result, unsigned int len)
+{
+ int r = 0;
+ unsigned char compressed = 0;
+ size_t in_len;
+#ifdef WITH_LZO
+ size_t out_len = len;
+#endif
+ unsigned char *in;
+ unsigned char *props = MEM_callocN(16 * sizeof(char), "tmp");
+
+ ptcache_file_read(pf, &compressed, 1, sizeof(unsigned char));
+ if (compressed) {
+ unsigned int size;
+ ptcache_file_read(pf, &size, 1, sizeof(unsigned int));
+ in_len = (size_t)size;
+ if (in_len==0) {
+ /* do nothing */
+ }
+ else {
+ in = (unsigned char *)MEM_callocN(sizeof(unsigned char)*in_len, "pointcache_compressed_buffer");
+ ptcache_file_read(pf, in, in_len, sizeof(unsigned char));
+#ifdef WITH_LZO
+ if (compressed == 1)
+ r = lzo1x_decompress_safe(in, (lzo_uint)in_len, result, (lzo_uint *)&out_len, NULL);
+#endif
+#ifdef WITH_LZMA
+ if (compressed == 2) {
+ size_t sizeOfIt;
+ size_t leni = in_len, leno = len;
+ ptcache_file_read(pf, &size, 1, sizeof(unsigned int));
+ sizeOfIt = (size_t)size;
+ ptcache_file_read(pf, props, sizeOfIt, sizeof(unsigned char));
+ r = LzmaUncompress(result, &leno, in, &leni, props, sizeOfIt);
+ }
+#endif
+ MEM_freeN(in);
+ }
+ }
+ else {
+ ptcache_file_read(pf, result, len, sizeof(unsigned char));
+ }
+
+ MEM_freeN(props);
+
+ return r;
+}
+static int ptcache_file_compressed_write(PTCacheFile *pf, unsigned char *in, unsigned int in_len, unsigned char *out, int mode)
+{
+ int r = 0;
+ unsigned char compressed = 0;
+ size_t out_len= 0;
+ unsigned char *props = MEM_callocN(16 * sizeof(char), "tmp");
+ size_t sizeOfIt = 5;
+
+ (void)mode; /* unused when building w/o compression */
+
+#ifdef WITH_LZO
+ out_len= LZO_OUT_LEN(in_len);
+ if (mode == 1) {
+ LZO_HEAP_ALLOC(wrkmem, LZO1X_MEM_COMPRESS);
+
+ r = lzo1x_1_compress(in, (lzo_uint)in_len, out, (lzo_uint *)&out_len, wrkmem);
+ if (!(r == LZO_E_OK) || (out_len >= in_len))
+ compressed = 0;
+ else
+ compressed = 1;
+ }
+#endif
+#ifdef WITH_LZMA
+ if (mode == 2) {
+
+ r = LzmaCompress(out, &out_len, in, in_len, //assume sizeof(char)==1....
+ props, &sizeOfIt, 5, 1 << 24, 3, 0, 2, 32, 2);
+
+ if (!(r == SZ_OK) || (out_len >= in_len))
+ compressed = 0;
+ else
+ compressed = 2;
+ }
+#endif
+
+ ptcache_file_write(pf, &compressed, 1, sizeof(unsigned char));
+ if (compressed) {
+ unsigned int size = out_len;
+ ptcache_file_write(pf, &size, 1, sizeof(unsigned int));
+ ptcache_file_write(pf, out, out_len, sizeof(unsigned char));
+ }
+ else
+ ptcache_file_write(pf, in, in_len, sizeof(unsigned char));
+
+ if (compressed == 2) {
+ unsigned int size = sizeOfIt;
+ ptcache_file_write(pf, &sizeOfIt, 1, sizeof(unsigned int));
+ ptcache_file_write(pf, props, size, sizeof(unsigned char));
+ }
+
+ MEM_freeN(props);
+
+ return r;
+}
+static int ptcache_file_read(PTCacheFile *pf, void *f, unsigned int tot, unsigned int size)
+{
+ return (fread(f, size, tot, pf->fp) == tot);
+}
+static int ptcache_file_write(PTCacheFile *pf, const void *f, unsigned int tot, unsigned int size)
+{
+ return (fwrite(f, size, tot, pf->fp) == tot);
+}
+static int ptcache_file_data_read(PTCacheFile *pf)
+{
+ int i;
+
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ if ((pf->data_types & (1<<i)) && !ptcache_file_read(pf, pf->cur[i], 1, ptcache_data_size[i]))
+ return 0;
+ }
+
+ return 1;
+}
+static int ptcache_file_data_write(PTCacheFile *pf)
+{
+ int i;
+
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ if ((pf->data_types & (1<<i)) && !ptcache_file_write(pf, pf->cur[i], 1, ptcache_data_size[i]))
+ return 0;
+ }
+
+ return 1;
+}
+static int ptcache_file_header_begin_read(PTCacheFile *pf)
+{
+ unsigned int typeflag=0;
+ int error=0;
+ char bphysics[8];
+
+ pf->data_types = 0;
+
+ if (fread(bphysics, sizeof(char), 8, pf->fp) != 8)
+ error = 1;
+
+ if (!error && !STREQLEN(bphysics, "BPHYSICS", 8))
+ error = 1;
+
+ if (!error && !fread(&typeflag, sizeof(unsigned int), 1, pf->fp))
+ error = 1;
+
+ pf->type = (typeflag & PTCACHE_TYPEFLAG_TYPEMASK);
+ pf->flag = (typeflag & PTCACHE_TYPEFLAG_FLAGMASK);
+
+ /* if there was an error set file as it was */
+ if (error)
+ fseek(pf->fp, 0, SEEK_SET);
+
+ return !error;
+}
+static int ptcache_file_header_begin_write(PTCacheFile *pf)
+{
+ const char *bphysics = "BPHYSICS";
+ unsigned int typeflag = pf->type + pf->flag;
+
+ if (fwrite(bphysics, sizeof(char), 8, pf->fp) != 8)
+ return 0;
+
+ if (!fwrite(&typeflag, sizeof(unsigned int), 1, pf->fp))
+ return 0;
+
+ return 1;
+}
+
+/* Data pointer handling */
+int BKE_ptcache_data_size(int data_type)
+{
+ return ptcache_data_size[data_type];
+}
+
+static void ptcache_file_pointers_init(PTCacheFile *pf)
+{
+ int data_types = pf->data_types;
+
+ pf->cur[BPHYS_DATA_INDEX] = (data_types & (1<<BPHYS_DATA_INDEX)) ? &pf->data.index : NULL;
+ pf->cur[BPHYS_DATA_LOCATION] = (data_types & (1<<BPHYS_DATA_LOCATION)) ? &pf->data.loc : NULL;
+ pf->cur[BPHYS_DATA_VELOCITY] = (data_types & (1<<BPHYS_DATA_VELOCITY)) ? &pf->data.vel : NULL;
+ pf->cur[BPHYS_DATA_ROTATION] = (data_types & (1<<BPHYS_DATA_ROTATION)) ? &pf->data.rot : NULL;
+ pf->cur[BPHYS_DATA_AVELOCITY] = (data_types & (1<<BPHYS_DATA_AVELOCITY))? &pf->data.ave : NULL;
+ pf->cur[BPHYS_DATA_SIZE] = (data_types & (1<<BPHYS_DATA_SIZE)) ? &pf->data.size : NULL;
+ pf->cur[BPHYS_DATA_TIMES] = (data_types & (1<<BPHYS_DATA_TIMES)) ? &pf->data.times : NULL;
+ pf->cur[BPHYS_DATA_BOIDS] = (data_types & (1<<BPHYS_DATA_BOIDS)) ? &pf->data.boids : NULL;
+}
+
+/* Check to see if point number "index" is in pm, uses binary search for index data. */
+int BKE_ptcache_mem_index_find(PTCacheMem *pm, unsigned int index)
+{
+ if (pm->totpoint > 0 && pm->data[BPHYS_DATA_INDEX]) {
+ unsigned int *data = pm->data[BPHYS_DATA_INDEX];
+ unsigned int mid, low = 0, high = pm->totpoint - 1;
+
+ if (index < *data || index > *(data+high))
+ return -1;
+
+ /* check simple case for continuous indexes first */
+ if (index-*data < high && data[index-*data] == index)
+ return index-*data;
+
+ while (low <= high) {
+ mid= (low + high)/2;
+
+ if (data[mid] > index)
+ high = mid - 1;
+ else if (data[mid] < index)
+ low = mid + 1;
+ else
+ return mid;
+ }
+
+ return -1;
+ }
+ else {
+ return (index < pm->totpoint ? index : -1);
+ }
+}
+
+void BKE_ptcache_mem_pointers_init(PTCacheMem *pm)
+{
+ int data_types = pm->data_types;
+ int i;
+
+ for (i=0; i<BPHYS_TOT_DATA; i++)
+ pm->cur[i] = ((data_types & (1<<i)) ? pm->data[i] : NULL);
+}
+
+void BKE_ptcache_mem_pointers_incr(PTCacheMem *pm)
+{
+ int i;
+
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ if (pm->cur[i])
+ pm->cur[i] = (char *)pm->cur[i] + ptcache_data_size[i];
+ }
+}
+int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm)
+{
+ int data_types = pm->data_types;
+ int i, index = BKE_ptcache_mem_index_find(pm, point_index);
+
+ if (index < 0) {
+ /* Can't give proper location without reallocation, so don't give any location.
+ * Some points will be cached improperly, but this only happens with simulation
+ * steps bigger than cache->step, so the cache has to be recalculated anyways
+ * at some point.
+ */
+ return 0;
+ }
+
+ for (i=0; i<BPHYS_TOT_DATA; i++)
+ pm->cur[i] = data_types & (1<<i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
+
+ return 1;
+}
+static void ptcache_data_alloc(PTCacheMem *pm)
+{
+ int data_types = pm->data_types;
+ int totpoint = pm->totpoint;
+ int i;
+
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ if (data_types & (1<<i))
+ pm->data[i] = MEM_callocN(totpoint * ptcache_data_size[i], "PTCache Data");
+ }
+}
+static void ptcache_data_free(PTCacheMem *pm)
+{
+ void **data = pm->data;
+ int i;
+
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ if (data[i])
+ MEM_freeN(data[i]);
+ }
+}
+static void ptcache_data_copy(void *from[], void *to[])
+{
+ int i;
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ /* note, durian file 03.4b_comp crashes if to[i] is not tested
+ * its NULL, not sure if this should be fixed elsewhere but for now its needed */
+ if (from[i] && to[i])
+ memcpy(to[i], from[i], ptcache_data_size[i]);
+ }
+}
+
+static void ptcache_extra_free(PTCacheMem *pm)
+{
+ PTCacheExtra *extra = pm->extradata.first;
+
+ if (extra) {
+ for (; extra; extra=extra->next) {
+ if (extra->data)
+ MEM_freeN(extra->data);
+ }
+
+ BLI_freelistN(&pm->extradata);
+ }
+}
+static int ptcache_old_elemsize(PTCacheID *pid)
+{
+ if (pid->type==PTCACHE_TYPE_SOFTBODY)
+ return 6 * sizeof(float);
+ else if (pid->type==PTCACHE_TYPE_PARTICLES)
+ return sizeof(ParticleKey);
+ else if (pid->type==PTCACHE_TYPE_CLOTH)
+ return 9 * sizeof(float);
+
+ return 0;
+}
+
+static void ptcache_find_frames_around(PTCacheID *pid, unsigned int frame, int *fra1, int *fra2)
+{
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ int cfra1=frame, cfra2=frame+1;
+
+ while (cfra1 >= pid->cache->startframe && !BKE_ptcache_id_exist(pid, cfra1))
+ cfra1--;
+
+ if (cfra1 < pid->cache->startframe)
+ cfra1 = 0;
+
+ while (cfra2 <= pid->cache->endframe && !BKE_ptcache_id_exist(pid, cfra2))
+ cfra2++;
+
+ if (cfra2 > pid->cache->endframe)
+ cfra2 = 0;
+
+ if (cfra1 && !cfra2) {
+ *fra1 = 0;
+ *fra2 = cfra1;
+ }
+ else {
+ *fra1 = cfra1;
+ *fra2 = cfra2;
+ }
+ }
+ else if (pid->cache->mem_cache.first) {
+ PTCacheMem *pm = pid->cache->mem_cache.first;
+ PTCacheMem *pm2 = pid->cache->mem_cache.last;
+
+ while (pm->next && pm->next->frame <= frame)
+ pm= pm->next;
+
+ if (pm2->frame < frame) {
+ pm2 = NULL;
+ }
+ else {
+ while (pm2->prev && pm2->prev->frame > frame) {
+ pm2= pm2->prev;
+ }
+ }
+
+ if (!pm2) {
+ *fra1 = 0;
+ *fra2 = pm->frame;
+ }
+ else {
+ *fra1 = pm->frame;
+ *fra2 = pm2->frame;
+ }
+ }
+}
+
+static PTCacheMem *ptcache_disk_frame_to_mem(PTCacheID *pid, int cfra)
+{
+ PTCacheFile *pf = ptcache_file_open(pid, PTCACHE_FILE_READ, cfra);
+ PTCacheMem *pm = NULL;
+ unsigned int i, error = 0;
+
+ if (pf == NULL)
+ return NULL;
+
+ if (!ptcache_file_header_begin_read(pf))
+ error = 1;
+
+ if (!error && (pf->type != pid->type || !pid->read_header(pf)))
+ error = 1;
+
+ if (!error) {
+ pm = MEM_callocN(sizeof(PTCacheMem), "Pointcache mem");
+
+ pm->totpoint = pf->totpoint;
+ pm->data_types = pf->data_types;
+ pm->frame = pf->frame;
+
+ ptcache_data_alloc(pm);
+
+ if (pf->flag & PTCACHE_TYPEFLAG_COMPRESS) {
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ unsigned int out_len = pm->totpoint*ptcache_data_size[i];
+ if (pf->data_types & (1<<i))
+ ptcache_file_compressed_read(pf, (unsigned char *)(pm->data[i]), out_len);
+ }
+ }
+ else {
+ BKE_ptcache_mem_pointers_init(pm);
+ ptcache_file_pointers_init(pf);
+
+ for (i=0; i<pm->totpoint; i++) {
+ if (!ptcache_file_data_read(pf)) {
+ error = 1;
+ break;
+ }
+ ptcache_data_copy(pf->cur, pm->cur);
+ BKE_ptcache_mem_pointers_incr(pm);
+ }
+ }
+ }
+
+ if (!error && pf->flag & PTCACHE_TYPEFLAG_EXTRADATA) {
+ unsigned int extratype = 0;
+
+ while (ptcache_file_read(pf, &extratype, 1, sizeof(unsigned int))) {
+ PTCacheExtra *extra = MEM_callocN(sizeof(PTCacheExtra), "Pointcache extradata");
+
+ extra->type = extratype;
+
+ ptcache_file_read(pf, &extra->totdata, 1, sizeof(unsigned int));
+
+ extra->data = MEM_callocN(extra->totdata * ptcache_extra_datasize[extra->type], "Pointcache extradata->data");
+
+ if (pf->flag & PTCACHE_TYPEFLAG_COMPRESS)
+ ptcache_file_compressed_read(pf, (unsigned char *)(extra->data), extra->totdata*ptcache_extra_datasize[extra->type]);
+ else
+ ptcache_file_read(pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
+
+ BLI_addtail(&pm->extradata, extra);
+ }
+ }
+
+ if (error && pm) {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ MEM_freeN(pm);
+ pm = NULL;
+ }
+
+ ptcache_file_close(pf);
+
+ if (error && G.debug & G_DEBUG)
+ printf("Error reading from disk cache\n");
+
+ return pm;
+}
+static int ptcache_mem_frame_to_disk(PTCacheID *pid, PTCacheMem *pm)
+{
+ PTCacheFile *pf = NULL;
+ unsigned int i, error = 0;
+
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_FRAME, pm->frame);
+
+ pf = ptcache_file_open(pid, PTCACHE_FILE_WRITE, pm->frame);
+
+ if (pf==NULL) {
+ if (G.debug & G_DEBUG)
+ printf("Error opening disk cache file for writing\n");
+ return 0;
+ }
+
+ pf->data_types = pm->data_types;
+ pf->totpoint = pm->totpoint;
+ pf->type = pid->type;
+ pf->flag = 0;
+
+ if (pm->extradata.first)
+ pf->flag |= PTCACHE_TYPEFLAG_EXTRADATA;
+
+ if (pid->cache->compression)
+ pf->flag |= PTCACHE_TYPEFLAG_COMPRESS;
+
+ if (!ptcache_file_header_begin_write(pf) || !pid->write_header(pf))
+ error = 1;
+
+ if (!error) {
+ if (pid->cache->compression) {
+ for (i=0; i<BPHYS_TOT_DATA; i++) {
+ if (pm->data[i]) {
+ unsigned int in_len = pm->totpoint*ptcache_data_size[i];
+ unsigned char *out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len) * 4, "pointcache_lzo_buffer");
+ ptcache_file_compressed_write(pf, (unsigned char *)(pm->data[i]), in_len, out, pid->cache->compression);
+ MEM_freeN(out);
+ }
+ }
+ }
+ else {
+ BKE_ptcache_mem_pointers_init(pm);
+ ptcache_file_pointers_init(pf);
+
+ for (i=0; i<pm->totpoint; i++) {
+ ptcache_data_copy(pm->cur, pf->cur);
+ if (!ptcache_file_data_write(pf)) {
+ error = 1;
+ break;
+ }
+ BKE_ptcache_mem_pointers_incr(pm);
+ }
+ }
+ }
+
+ if (!error && pm->extradata.first) {
+ PTCacheExtra *extra = pm->extradata.first;
+
+ for (; extra; extra=extra->next) {
+ if (extra->data == NULL || extra->totdata == 0)
+ continue;
+
+ ptcache_file_write(pf, &extra->type, 1, sizeof(unsigned int));
+ ptcache_file_write(pf, &extra->totdata, 1, sizeof(unsigned int));
+
+ if (pid->cache->compression) {
+ unsigned int in_len = extra->totdata * ptcache_extra_datasize[extra->type];
+ unsigned char *out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len) * 4, "pointcache_lzo_buffer");
+ ptcache_file_compressed_write(pf, (unsigned char *)(extra->data), in_len, out, pid->cache->compression);
+ MEM_freeN(out);
+ }
+ else {
+ ptcache_file_write(pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
+ }
+ }
+ }
+
+ ptcache_file_close(pf);
+
+ if (error && G.debug & G_DEBUG)
+ printf("Error writing to disk cache\n");
+
+ return error==0;
+}
+
+static int ptcache_read_stream(PTCacheID *pid, int cfra)
+{
+ PTCacheFile *pf = ptcache_file_open(pid, PTCACHE_FILE_READ, cfra);
+ int error = 0;
+
+ if (pid->read_stream == NULL)
+ return 0;
+
+ if (pf == NULL) {
+ if (G.debug & G_DEBUG)
+ printf("Error opening disk cache file for reading\n");
+ return 0;
+ }
+
+ if (!ptcache_file_header_begin_read(pf)) {
+ pid->error(pid->calldata, "Failed to read point cache file");
+ error = 1;
+ }
+ else if (pf->type != pid->type) {
+ pid->error(pid->calldata, "Point cache file has wrong type");
+ error = 1;
+ }
+ else if (!pid->read_header(pf)) {
+ pid->error(pid->calldata, "Failed to read point cache file header");
+ error = 1;
+ }
+ else if (pf->totpoint != pid->totpoint(pid->calldata, cfra)) {
+ pid->error(pid->calldata, "Number of points in cache does not match mesh");
+ error = 1;
+ }
+
+ if (!error) {
+ ptcache_file_pointers_init(pf);
+
+ // we have stream reading here
+ if (!pid->read_stream(pf, pid->calldata)) {
+ pid->error(pid->calldata, "Failed to read point cache file data");
+ error = 1;
+ }
+ }
+
+ ptcache_file_close(pf);
+
+ return error == 0;
+}
+
+static int ptcache_read_openvdb_stream(PTCacheID *pid, int cfra)
+{
+#ifdef WITH_OPENVDB
+ char filename[FILE_MAX * 2];
+
+ /* save blend file before using disk pointcache */
+ if (!G.relbase_valid && (pid->cache->flag & PTCACHE_EXTERNAL) == 0)
+ return 0;
+
+ ptcache_filename(pid, filename, cfra, 1, 1);
+
+ if (!BLI_exists(filename)) {
+ return 0;
+ }
+
+ struct OpenVDBReader *reader = OpenVDBReader_create();
+ OpenVDBReader_open(reader, filename);
+
+ if (!pid->read_openvdb_stream(reader, pid->calldata)) {
+ return 0;
+ }
+
+ return 1;
+#else
+ UNUSED_VARS(pid, cfra);
+ return 0;
+#endif
+}
+
+static int ptcache_read(PTCacheID *pid, int cfra)
+{
+ PTCacheMem *pm = NULL;
+ int i;
+ int *index = &i;
+
+ /* get a memory cache to read from */
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ pm = ptcache_disk_frame_to_mem(pid, cfra);
+ }
+ else {
+ pm = pid->cache->mem_cache.first;
+
+ while (pm && pm->frame != cfra)
+ pm = pm->next;
+ }
+
+ /* read the cache */
+ if (pm) {
+ int totpoint = pm->totpoint;
+
+ if ((pid->data_types & (1<<BPHYS_DATA_INDEX)) == 0) {
+ int pid_totpoint = pid->totpoint(pid->calldata, cfra);
+
+ if (totpoint != pid_totpoint) {
+ pid->error(pid->calldata, "Number of points in cache does not match mesh");
+ totpoint = MIN2(totpoint, pid_totpoint);
+ }
+ }
+
+ BKE_ptcache_mem_pointers_init(pm);
+
+ for (i=0; i<totpoint; i++) {
+ if (pm->data_types & (1<<BPHYS_DATA_INDEX))
+ index = pm->cur[BPHYS_DATA_INDEX];
+
+ pid->read_point(*index, pid->calldata, pm->cur, (float)pm->frame, NULL);
+
+ BKE_ptcache_mem_pointers_incr(pm);
+ }
+
+ if (pid->read_extra_data && pm->extradata.first)
+ pid->read_extra_data(pid->calldata, pm, (float)pm->frame);
+
+ /* clean up temporary memory cache */
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ MEM_freeN(pm);
+ }
+ }
+
+ return 1;
+}
+static int ptcache_interpolate(PTCacheID *pid, float cfra, int cfra1, int cfra2)
+{
+ PTCacheMem *pm = NULL;
+ int i;
+ int *index = &i;
+
+ /* get a memory cache to read from */
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ pm = ptcache_disk_frame_to_mem(pid, cfra2);
+ }
+ else {
+ pm = pid->cache->mem_cache.first;
+
+ while (pm && pm->frame != cfra2)
+ pm = pm->next;
+ }
+
+ /* read the cache */
+ if (pm) {
+ int totpoint = pm->totpoint;
+
+ if ((pid->data_types & (1<<BPHYS_DATA_INDEX)) == 0) {
+ int pid_totpoint = pid->totpoint(pid->calldata, (int)cfra);
+
+ if (totpoint != pid_totpoint) {
+ pid->error(pid->calldata, "Number of points in cache does not match mesh");
+ totpoint = MIN2(totpoint, pid_totpoint);
+ }
+ }
+
+ BKE_ptcache_mem_pointers_init(pm);
+
+ for (i=0; i<totpoint; i++) {
+ if (pm->data_types & (1<<BPHYS_DATA_INDEX))
+ index = pm->cur[BPHYS_DATA_INDEX];
+
+ pid->interpolate_point(*index, pid->calldata, pm->cur, cfra, (float)cfra1, (float)cfra2, NULL);
+ BKE_ptcache_mem_pointers_incr(pm);
+ }
+
+ if (pid->interpolate_extra_data && pm->extradata.first)
+ pid->interpolate_extra_data(pid->calldata, pm, cfra, (float)cfra1, (float)cfra2);
+
+ /* clean up temporary memory cache */
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ MEM_freeN(pm);
+ }
+ }
+
+ return 1;
+}
+/* reads cache from disk or memory */
+/* possible to get old or interpolated result */
+int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old)
+{
+ int cfrai = (int)floor(cfra), cfra1=0, cfra2=0;
+ int ret = 0;
+
+ /* nothing to read to */
+ if (pid->totpoint(pid->calldata, cfrai) == 0)
+ return 0;
+
+ if (pid->cache->flag & PTCACHE_READ_INFO) {
+ pid->cache->flag &= ~PTCACHE_READ_INFO;
+ ptcache_read(pid, 0);
+ }
+
+ /* first check if we have the actual frame cached */
+ if (cfra == (float)cfrai && BKE_ptcache_id_exist(pid, cfrai))
+ cfra1 = cfrai;
+
+ /* no exact cache frame found so try to find cached frames around cfra */
+ if (cfra1 == 0)
+ ptcache_find_frames_around(pid, cfrai, &cfra1, &cfra2);
+
+ if (cfra1 == 0 && cfra2 == 0)
+ return 0;
+
+ /* don't read old cache if already simulated past cached frame */
+ if (no_extrapolate_old) {
+ if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe)
+ return 0;
+ if (cfra1 && cfra1 == cfra2)
+ return 0;
+ }
+ else {
+ /* avoid calling interpolate between the same frame values */
+ if (cfra1 && cfra1 == cfra2)
+ cfra1 = 0;
+ }
+
+ if (cfra1) {
+ if (pid->file_type == PTCACHE_FILE_OPENVDB && pid->read_openvdb_stream) {
+ if (!ptcache_read_openvdb_stream(pid, cfra1)) {
+ return 0;
+ }
+ }
+ else if (pid->read_stream) {
+ if (!ptcache_read_stream(pid, cfra1))
+ return 0;
+ }
+ else if (pid->read_point)
+ ptcache_read(pid, cfra1);
+ }
+
+ if (cfra2) {
+ if (pid->file_type == PTCACHE_FILE_OPENVDB && pid->read_openvdb_stream) {
+ if (!ptcache_read_openvdb_stream(pid, cfra2)) {
+ return 0;
+ }
+ }
+ else if (pid->read_stream) {
+ if (!ptcache_read_stream(pid, cfra2))
+ return 0;
+ }
+ else if (pid->read_point) {
+ if (cfra1 && cfra2 && pid->interpolate_point)
+ ptcache_interpolate(pid, cfra, cfra1, cfra2);
+ else
+ ptcache_read(pid, cfra2);
+ }
+ }
+
+ if (cfra1)
+ ret = (cfra2 ? PTCACHE_READ_INTERPOLATED : PTCACHE_READ_EXACT);
+ else if (cfra2) {
+ ret = PTCACHE_READ_OLD;
+ pid->cache->simframe = cfra2;
+ }
+
+ cfrai = (int)cfra;
+ /* clear invalid cache frames so that better stuff can be simulated */
+ if (pid->cache->flag & PTCACHE_OUTDATED) {
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, cfrai);
+ }
+ else if (pid->cache->flag & PTCACHE_FRAMES_SKIPPED) {
+ if (cfra <= pid->cache->last_exact)
+ pid->cache->flag &= ~PTCACHE_FRAMES_SKIPPED;
+
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, MAX2(cfrai, pid->cache->last_exact));
+ }
+
+ return ret;
+}
+static int ptcache_write_stream(PTCacheID *pid, int cfra, int totpoint)
+{
+ PTCacheFile *pf = NULL;
+ int error = 0;
+
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_FRAME, cfra);
+
+ pf = ptcache_file_open(pid, PTCACHE_FILE_WRITE, cfra);
+
+ if (pf==NULL) {
+ if (G.debug & G_DEBUG)
+ printf("Error opening disk cache file for writing\n");
+ return 0;
+ }
+
+ pf->data_types = pid->data_types;
+ pf->totpoint = totpoint;
+ pf->type = pid->type;
+ pf->flag = 0;
+
+ if (!error && (!ptcache_file_header_begin_write(pf) || !pid->write_header(pf)))
+ error = 1;
+
+ if (!error && pid->write_stream)
+ pid->write_stream(pf, pid->calldata);
+
+ ptcache_file_close(pf);
+
+ if (error && G.debug & G_DEBUG)
+ printf("Error writing to disk cache\n");
+
+ return error == 0;
+}
+static int ptcache_write_openvdb_stream(PTCacheID *pid, int cfra)
+{
+#ifdef WITH_OPENVDB
+ struct OpenVDBWriter *writer = OpenVDBWriter_create();
+ char filename[FILE_MAX * 2];
+
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_FRAME, cfra);
+
+ ptcache_filename(pid, filename, cfra, 1, 1);
+ BLI_make_existing_file(filename);
+
+ int error = pid->write_openvdb_stream(writer, pid->calldata);
+
+ OpenVDBWriter_write(writer, filename);
+ OpenVDBWriter_free(writer);
+
+ return error == 0;
+#else
+ UNUSED_VARS(pid, cfra);
+ return 0;
+#endif
+}
+static int ptcache_write(PTCacheID *pid, int cfra, int overwrite)
+{
+ PointCache *cache = pid->cache;
+ PTCacheMem *pm=NULL, *pm2=NULL;
+ int totpoint = pid->totpoint(pid->calldata, cfra);
+ int i, error = 0;
+
+ pm = MEM_callocN(sizeof(PTCacheMem), "Pointcache mem");
+
+ pm->totpoint = pid->totwrite(pid->calldata, cfra);
+ pm->data_types = cfra ? pid->data_types : pid->info_types;
+
+ ptcache_data_alloc(pm);
+ BKE_ptcache_mem_pointers_init(pm);
+
+ if (overwrite) {
+ if (cache->flag & PTCACHE_DISK_CACHE) {
+ int fra = cfra-1;
+
+ while (fra >= cache->startframe && !BKE_ptcache_id_exist(pid, fra))
+ fra--;
+
+ pm2 = ptcache_disk_frame_to_mem(pid, fra);
+ }
+ else
+ pm2 = cache->mem_cache.last;
+ }
+
+ if (pid->write_point) {
+ for (i=0; i<totpoint; i++) {
+ int write = pid->write_point(i, pid->calldata, pm->cur, cfra);
+ if (write) {
+ BKE_ptcache_mem_pointers_incr(pm);
+
+ /* newly born particles have to be copied to previous cached frame */
+ if (overwrite && write == 2 && pm2 && BKE_ptcache_mem_pointers_seek(i, pm2))
+ pid->write_point(i, pid->calldata, pm2->cur, cfra);
+ }
+ }
+ }
+
+ if (pid->write_extra_data)
+ pid->write_extra_data(pid->calldata, pm, cfra);
+
+ pm->frame = cfra;
+
+ if (cache->flag & PTCACHE_DISK_CACHE) {
+ error += !ptcache_mem_frame_to_disk(pid, pm);
+
+ // if (pm) /* pm is always set */
+ {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ MEM_freeN(pm);
+ }
+
+ if (pm2) {
+ error += !ptcache_mem_frame_to_disk(pid, pm2);
+ ptcache_data_free(pm2);
+ ptcache_extra_free(pm2);
+ MEM_freeN(pm2);
+ }
+ }
+ else {
+ BLI_addtail(&cache->mem_cache, pm);
+ }
+
+ return error;
+}
+static int ptcache_write_needed(PTCacheID *pid, int cfra, int *overwrite)
+{
+ PointCache *cache = pid->cache;
+ int ofra = 0, efra = cache->endframe;
+
+ /* always start from scratch on the first frame */
+ if (cfra && cfra == cache->startframe) {
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, cfra);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+ return 1;
+ }
+
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ if (cfra==0 && cache->startframe > 0)
+ return 1;
+
+ /* find last cached frame */
+ while (efra > cache->startframe && !BKE_ptcache_id_exist(pid, efra))
+ efra--;
+
+ /* find second last cached frame */
+ ofra = efra-1;
+ while (ofra > cache->startframe && !BKE_ptcache_id_exist(pid, ofra))
+ ofra--;
+ }
+ else {
+ PTCacheMem *pm = cache->mem_cache.last;
+ /* don't write info file in memory */
+ if (cfra == 0)
+ return 0;
+
+ if (pm == NULL)
+ return 1;
+
+ efra = pm->frame;
+ ofra = (pm->prev ? pm->prev->frame : efra - cache->step);
+ }
+
+ if (efra >= cache->startframe && cfra > efra) {
+ if (ofra >= cache->startframe && efra - ofra < cache->step) {
+ /* overwrite previous frame */
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_FRAME, efra);
+ *overwrite = 1;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+/* writes cache to disk or memory */
+int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra)
+{
+ PointCache *cache = pid->cache;
+ int totpoint = pid->totpoint(pid->calldata, cfra);
+ int overwrite = 0, error = 0;
+
+ if (totpoint == 0 || (cfra ? pid->data_types == 0 : pid->info_types == 0))
+ return 0;
+
+ if (ptcache_write_needed(pid, cfra, &overwrite)==0)
+ return 0;
+
+ if (pid->file_type == PTCACHE_FILE_OPENVDB && pid->write_openvdb_stream) {
+ ptcache_write_openvdb_stream(pid, cfra);
+ }
+ else if (pid->write_stream) {
+ ptcache_write_stream(pid, cfra, totpoint);
+ }
+ else if (pid->write_point) {
+ error += ptcache_write(pid, cfra, overwrite);
+ }
+
+ /* Mark frames skipped if more than 1 frame forwards since last non-skipped frame. */
+ if (cfra - cache->last_exact == 1 || cfra == cache->startframe) {
+ cache->last_exact = cfra;
+ cache->flag &= ~PTCACHE_FRAMES_SKIPPED;
+ }
+ /* Don't mark skipped when writing info file (frame 0) */
+ else if (cfra)
+ cache->flag |= PTCACHE_FRAMES_SKIPPED;
+
+ /* Update timeline cache display */
+ if (cfra && cache->cached_frames)
+ cache->cached_frames[cfra-cache->startframe] = 1;
+
+ BKE_ptcache_update_info(pid);
+
+ return !error;
+}
+/* youll need to close yourself after!
+ * mode - PTCACHE_CLEAR_ALL,
+ */
+
+/* Clears & resets */
+void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
+{
+ unsigned int len; /* store the length of the string */
+ unsigned int sta, end;
+
+ /* mode is same as fopen's modes */
+ DIR *dir;
+ struct dirent *de;
+ char path[MAX_PTCACHE_PATH];
+ char filename[MAX_PTCACHE_FILE];
+ char path_full[MAX_PTCACHE_FILE];
+ char ext[MAX_PTCACHE_PATH];
+
+ if (!pid || !pid->cache || pid->cache->flag & PTCACHE_BAKED)
+ return;
+
+ if (pid->cache->flag & PTCACHE_IGNORE_CLEAR)
+ return;
+
+ sta = pid->cache->startframe;
+ end = pid->cache->endframe;
+
+#ifndef DURIAN_POINTCACHE_LIB_OK
+ /* don't allow clearing for linked objects */
+ if (pid->ob->id.lib)
+ return;
+#endif
+
+ /*if (!G.relbase_valid) return; *//* save blend file before using pointcache */
+
+ const char *fext = ptcache_file_extension(pid);
+
+ /* clear all files in the temp dir with the prefix of the ID and the ".bphys" suffix */
+ switch (mode) {
+ case PTCACHE_CLEAR_ALL:
+ case PTCACHE_CLEAR_BEFORE:
+ case PTCACHE_CLEAR_AFTER:
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ ptcache_path(pid, path);
+
+ dir = opendir(path);
+ if (dir==NULL)
+ return;
+
+ len = ptcache_filename(pid, filename, cfra, 0, 0); /* no path */
+ /* append underscore terminator to ensure we don't match similar names
+ * from objects whose names start with the same prefix
+ */
+ if (len < sizeof(filename) - 2) {
+ BLI_strncpy(filename + len, "_", sizeof(filename) - 2 - len);
+ len += 1;
+ }
+
+ BLI_snprintf(ext, sizeof(ext), "_%02u%s", pid->stack_index, fext);
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strstr(de->d_name, ext)) { /* do we have the right extension?*/
+ if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */
+ if (mode == PTCACHE_CLEAR_ALL) {
+ pid->cache->last_exact = MIN2(pid->cache->startframe, 0);
+ BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
+ BLI_delete(path_full, false, false);
+ }
+ else {
+ /* read the number of the file */
+ const int frame = ptcache_frame_from_filename(de->d_name, ext);
+
+ if (frame != -1) {
+ if ((mode == PTCACHE_CLEAR_BEFORE && frame < cfra) ||
+ (mode == PTCACHE_CLEAR_AFTER && frame > cfra))
+ {
+
+ BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
+ BLI_delete(path_full, false, false);
+ if (pid->cache->cached_frames && frame >=sta && frame <= end)
+ pid->cache->cached_frames[frame-sta] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ closedir(dir);
+
+ if (mode == PTCACHE_CLEAR_ALL && pid->cache->cached_frames)
+ memset(pid->cache->cached_frames, 0, MEM_allocN_len(pid->cache->cached_frames));
+ }
+ else {
+ PTCacheMem *pm= pid->cache->mem_cache.first;
+ PTCacheMem *link= NULL;
+
+ if (mode == PTCACHE_CLEAR_ALL) {
+ /*we want startframe if the cache starts before zero*/
+ pid->cache->last_exact = MIN2(pid->cache->startframe, 0);
+ for (; pm; pm=pm->next) {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ }
+ BLI_freelistN(&pid->cache->mem_cache);
+
+ if (pid->cache->cached_frames)
+ memset(pid->cache->cached_frames, 0, MEM_allocN_len(pid->cache->cached_frames));
+ }
+ else {
+ while (pm) {
+ if ((mode == PTCACHE_CLEAR_BEFORE && pm->frame < cfra) ||
+ (mode == PTCACHE_CLEAR_AFTER && pm->frame > cfra))
+ {
+ link = pm;
+ if (pid->cache->cached_frames && pm->frame >=sta && pm->frame <= end)
+ pid->cache->cached_frames[pm->frame-sta] = 0;
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ pm = pm->next;
+ BLI_freelinkN(&pid->cache->mem_cache, link);
+ }
+ else
+ pm = pm->next;
+ }
+ }
+ }
+ break;
+
+ case PTCACHE_CLEAR_FRAME:
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ if (BKE_ptcache_id_exist(pid, cfra)) {
+ ptcache_filename(pid, filename, cfra, 1, 1); /* no path */
+ BLI_delete(filename, false, false);
+ }
+ }
+ else {
+ PTCacheMem *pm = pid->cache->mem_cache.first;
+
+ for (; pm; pm=pm->next) {
+ if (pm->frame == cfra) {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ BLI_freelinkN(&pid->cache->mem_cache, pm);
+ break;
+ }
+ }
+ }
+ if (pid->cache->cached_frames && cfra >= sta && cfra <= end)
+ pid->cache->cached_frames[cfra-sta] = 0;
+ break;
+ }
+
+ BKE_ptcache_update_info(pid);
+}
+int BKE_ptcache_id_exist(PTCacheID *pid, int cfra)
+{
+ if (!pid->cache)
+ return 0;
+
+ if (cfra<pid->cache->startframe || cfra > pid->cache->endframe)
+ return 0;
+
+ if (pid->cache->cached_frames && pid->cache->cached_frames[cfra-pid->cache->startframe]==0)
+ return 0;
+
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ char filename[MAX_PTCACHE_FILE];
+
+ ptcache_filename(pid, filename, cfra, 1, 1);
+
+ return BLI_exists(filename);
+ }
+ else {
+ PTCacheMem *pm = pid->cache->mem_cache.first;
+
+ for (; pm; pm=pm->next) {
+ if (pm->frame==cfra)
+ return 1;
+ }
+ return 0;
+ }
+}
+void BKE_ptcache_id_time(PTCacheID *pid, Scene *scene, float cfra, int *startframe, int *endframe, float *timescale)
+{
+ /* Object *ob; */ /* UNUSED */
+ PointCache *cache;
+ /* float offset; unused for now */
+ float time, nexttime;
+
+ /* TODO: this has to be sorted out once bsystem_time gets redone, */
+ /* now caches can handle interpolating etc. too - jahka */
+
+ /* time handling for point cache:
+ * - simulation time is scaled by result of bsystem_time
+ * - for offsetting time only time offset is taken into account, since
+ * that's always the same and can't be animated. a timeoffset which
+ * varies over time is not simple to support.
+ * - field and motion blur offsets are currently ignored, proper solution
+ * is probably to interpolate results from two frames for that ..
+ */
+
+ /* ob= pid->ob; */ /* UNUSED */
+ cache= pid->cache;
+
+ if (timescale) {
+ time= BKE_scene_frame_get(scene);
+ nexttime = BKE_scene_frame_get_from_ctime(scene, CFRA + 1.0f);
+
+ *timescale= MAX2(nexttime - time, 0.0f);
+ }
+
+ if (startframe && endframe) {
+ *startframe= cache->startframe;
+ *endframe= cache->endframe;
+
+ /* TODO: time handling with object offsets and simulated vs. cached
+ * particles isn't particularly easy, so for now what you see is what
+ * you get. In the future point cache could handle the whole particle
+ * system timing. */
+#if 0
+ if ((ob->partype & PARSLOW)==0) {
+ offset= ob->sf;
+
+ *startframe += (int)(offset+0.5f);
+ *endframe += (int)(offset+0.5f);
+ }
+#endif
+ }
+
+ /* verify cached_frames array is up to date */
+ if (cache->cached_frames) {
+ if (MEM_allocN_len(cache->cached_frames) != sizeof(char) * (cache->endframe-cache->startframe+1)) {
+ MEM_freeN(cache->cached_frames);
+ cache->cached_frames = NULL;
+ }
+ }
+
+ if (cache->cached_frames==NULL && cache->endframe > cache->startframe) {
+ unsigned int sta=cache->startframe;
+ unsigned int end=cache->endframe;
+
+ cache->cached_frames = MEM_callocN(sizeof(char) * (cache->endframe-cache->startframe+1), "cached frames array");
+
+ if (pid->cache->flag & PTCACHE_DISK_CACHE) {
+ /* mode is same as fopen's modes */
+ DIR *dir;
+ struct dirent *de;
+ char path[MAX_PTCACHE_PATH];
+ char filename[MAX_PTCACHE_FILE];
+ char ext[MAX_PTCACHE_PATH];
+ unsigned int len; /* store the length of the string */
+
+ ptcache_path(pid, path);
+
+ len = ptcache_filename(pid, filename, (int)cfra, 0, 0); /* no path */
+
+ dir = opendir(path);
+ if (dir==NULL)
+ return;
+
+ const char *fext = ptcache_file_extension(pid);
+
+ BLI_snprintf(ext, sizeof(ext), "_%02u%s", pid->stack_index, fext);
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strstr(de->d_name, ext)) { /* do we have the right extension?*/
+ if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */
+ /* read the number of the file */
+ const int frame = ptcache_frame_from_filename(de->d_name, ext);
+
+ if ((frame != -1) && (frame >= sta && frame <= end)) {
+ cache->cached_frames[frame-sta] = 1;
+ }
+ }
+ }
+ }
+ closedir(dir);
+ }
+ else {
+ PTCacheMem *pm= pid->cache->mem_cache.first;
+
+ while (pm) {
+ if (pm->frame >= sta && pm->frame <= end)
+ cache->cached_frames[pm->frame-sta] = 1;
+ pm = pm->next;
+ }
+ }
+ }
+}
+int BKE_ptcache_id_reset(Scene *scene, PTCacheID *pid, int mode)
+{
+ PointCache *cache;
+ int reset, clear, after;
+
+ if (!pid->cache)
+ return 0;
+
+ cache= pid->cache;
+ reset= 0;
+ clear= 0;
+ after= 0;
+
+ if (mode == PTCACHE_RESET_DEPSGRAPH) {
+ if (!(cache->flag & PTCACHE_BAKED)) {
+
+ after= 1;
+ }
+
+ cache->flag |= PTCACHE_OUTDATED;
+ }
+ else if (mode == PTCACHE_RESET_BAKED) {
+ cache->flag |= PTCACHE_OUTDATED;
+ }
+ else if (mode == PTCACHE_RESET_OUTDATED) {
+ reset = 1;
+
+ if (cache->flag & PTCACHE_OUTDATED && !(cache->flag & PTCACHE_BAKED)) {
+ clear= 1;
+ cache->flag &= ~PTCACHE_OUTDATED;
+ }
+ }
+
+ if (reset) {
+ BKE_ptcache_invalidate(cache);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+
+ if (pid->type == PTCACHE_TYPE_CLOTH)
+ cloth_free_modifier(pid->calldata);
+ else if (pid->type == PTCACHE_TYPE_SOFTBODY)
+ sbFreeSimulation(pid->calldata);
+ else if (pid->type == PTCACHE_TYPE_PARTICLES)
+ psys_reset(pid->calldata, PSYS_RESET_DEPSGRAPH);
+#if 0
+ else if (pid->type == PTCACHE_TYPE_SMOKE_DOMAIN)
+ smokeModifier_reset(pid->calldata);
+ else if (pid->type == PTCACHE_TYPE_SMOKE_HIGHRES)
+ smokeModifier_reset_turbulence(pid->calldata);
+#endif
+ else if (pid->type == PTCACHE_TYPE_DYNAMICPAINT)
+ dynamicPaint_clearSurface(scene, (DynamicPaintSurface*)pid->calldata);
+ }
+ if (clear)
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+ else if (after)
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_AFTER, CFRA);
+
+ return (reset || clear || after);
+}
+int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode)
+{
+ PTCacheID pid;
+ ParticleSystem *psys;
+ ModifierData *md;
+ int reset, skip;
+
+ reset= 0;
+ skip= 0;
+
+ if (ob->soft) {
+ BKE_ptcache_id_from_softbody(&pid, ob, ob->soft);
+ reset |= BKE_ptcache_id_reset(scene, &pid, mode);
+ }
+
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ /* children or just redo can be calculated without resetting anything */
+ if (psys->recalc & PSYS_RECALC_REDO || psys->recalc & PSYS_RECALC_CHILD)
+ skip = 1;
+ /* Baked cloth hair has to be checked too, because we don't want to reset */
+ /* particles or cloth in that case -jahka */
+ else if (psys->clmd) {
+ BKE_ptcache_id_from_cloth(&pid, ob, psys->clmd);
+ if (mode == PSYS_RESET_ALL || !(psys->part->type == PART_HAIR && (pid.cache->flag & PTCACHE_BAKED)))
+ reset |= BKE_ptcache_id_reset(scene, &pid, mode);
+ else
+ skip = 1;
+ }
+
+ if (skip == 0 && psys->part) {
+ BKE_ptcache_id_from_particles(&pid, ob, psys);
+ reset |= BKE_ptcache_id_reset(scene, &pid, mode);
+ }
+ }
+
+ for (md=ob->modifiers.first; md; md=md->next) {
+ if (md->type == eModifierType_Cloth) {
+ BKE_ptcache_id_from_cloth(&pid, ob, (ClothModifierData*)md);
+ reset |= BKE_ptcache_id_reset(scene, &pid, mode);
+ }
+ if (md->type == eModifierType_Smoke) {
+ SmokeModifierData *smd = (SmokeModifierData *)md;
+ if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
+ BKE_ptcache_id_from_smoke(&pid, ob, (SmokeModifierData*)md);
+ reset |= BKE_ptcache_id_reset(scene, &pid, mode);
+ }
+ }
+ if (md->type == eModifierType_DynamicPaint) {
+ DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
+ if (pmd->canvas) {
+ DynamicPaintSurface *surface = pmd->canvas->surfaces.first;
+
+ for (; surface; surface=surface->next) {
+ BKE_ptcache_id_from_dynamicpaint(&pid, ob, surface);
+ reset |= BKE_ptcache_id_reset(scene, &pid, mode);
+ }
+ }
+ }
+ }
+
+ if (scene->rigidbody_world && (ob->rigidbody_object || ob->rigidbody_constraint)) {
+ if (ob->rigidbody_object)
+ ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_RESHAPE;
+ BKE_ptcache_id_from_rigidbody(&pid, ob, scene->rigidbody_world);
+ /* only flag as outdated, resetting should happen on start frame */
+ pid.cache->flag |= PTCACHE_OUTDATED;
+ }
+
+ if (ob->type == OB_ARMATURE)
+ BIK_clear_cache(ob->pose);
+
+ return reset;
+}
+
+/* Use this when quitting blender, with unsaved files */
+void BKE_ptcache_remove(void)
+{
+ char path[MAX_PTCACHE_PATH];
+ char path_full[MAX_PTCACHE_PATH];
+ int rmdir = 1;
+
+ ptcache_path(NULL, path);
+
+ if (BLI_exists(path)) {
+ /* The pointcache dir exists? - remove all pointcache */
+
+ DIR *dir;
+ struct dirent *de;
+
+ dir = opendir(path);
+ if (dir==NULL)
+ return;
+
+ while ((de = readdir(dir)) != NULL) {
+ if (FILENAME_IS_CURRPAR(de->d_name)) {
+ /* do nothing */
+ }
+ else if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/
+ BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
+ BLI_delete(path_full, false, false);
+ }
+ else {
+ rmdir = 0; /* unknown file, don't remove the dir */
+ }
+ }
+
+ closedir(dir);
+ }
+ else {
+ rmdir = 0; /* path dosnt exist */
+ }
+
+ if (rmdir) {
+ BLI_delete(path, true, false);
+ }
+}
+
+/* Point Cache handling */
+
+PointCache *BKE_ptcache_add(ListBase *ptcaches)
+{
+ PointCache *cache;
+
+ cache= MEM_callocN(sizeof(PointCache), "PointCache");
+ cache->startframe= 1;
+ cache->endframe= 250;
+ cache->step = 1;
+ cache->index = -1;
+
+ BLI_addtail(ptcaches, cache);
+
+ return cache;
+}
+
+void BKE_ptcache_free_mem(ListBase *mem_cache)
+{
+ PTCacheMem *pm = mem_cache->first;
+
+ if (pm) {
+ for (; pm; pm=pm->next) {
+ ptcache_data_free(pm);
+ ptcache_extra_free(pm);
+ }
+
+ BLI_freelistN(mem_cache);
+ }
+}
+void BKE_ptcache_free(PointCache *cache)
+{
+ BKE_ptcache_free_mem(&cache->mem_cache);
+ if (cache->edit && cache->free_edit)
+ cache->free_edit(cache->edit);
+ if (cache->cached_frames)
+ MEM_freeN(cache->cached_frames);
+ MEM_freeN(cache);
+}
+void BKE_ptcache_free_list(ListBase *ptcaches)
+{
+ PointCache *cache;
+
+ while ((cache = BLI_pophead(ptcaches))) {
+ BKE_ptcache_free(cache);
+ }
+}
+
+static PointCache *ptcache_copy(PointCache *cache, bool copy_data)
+{
+ PointCache *ncache;
+
+ ncache= MEM_dupallocN(cache);
+
+ BLI_listbase_clear(&ncache->mem_cache);
+
+ if (copy_data == false) {
+ ncache->cached_frames = NULL;
+
+ /* flag is a mix of user settings and simulator/baking state */
+ ncache->flag= ncache->flag & (PTCACHE_DISK_CACHE|PTCACHE_EXTERNAL|PTCACHE_IGNORE_LIBPATH);
+ ncache->simframe= 0;
+ }
+ else {
+ PTCacheMem *pm;
+
+ for (pm = cache->mem_cache.first; pm; pm = pm->next) {
+ PTCacheMem *pmn = MEM_dupallocN(pm);
+ int i;
+
+ for (i = 0; i < BPHYS_TOT_DATA; i++) {
+ if (pmn->data[i])
+ pmn->data[i] = MEM_dupallocN(pm->data[i]);
+ }
+
+ BKE_ptcache_mem_pointers_init(pm);
+
+ BLI_addtail(&ncache->mem_cache, pmn);
+ }
+
+ if (ncache->cached_frames)
+ ncache->cached_frames = MEM_dupallocN(cache->cached_frames);
+ }
+
+ /* hmm, should these be copied over instead? */
+ ncache->edit = NULL;
+
+ return ncache;
+}
+
+/* returns first point cache */
+PointCache *BKE_ptcache_copy_list(ListBase *ptcaches_new, const ListBase *ptcaches_old, bool copy_data)
+{
+ PointCache *cache = ptcaches_old->first;
+
+ BLI_listbase_clear(ptcaches_new);
+
+ for (; cache; cache=cache->next)
+ BLI_addtail(ptcaches_new, ptcache_copy(cache, copy_data));
+
+ return ptcaches_new->first;
+}
+
+/* Disabled this code; this is being called on scene_update_tagged, and that in turn gets called on
+ * every user action changing stuff, and then it runs a complete bake??? (ton) */
+
+/* Baking */
+void BKE_ptcache_quick_cache_all(Main *bmain, Scene *scene)
+{
+ PTCacheBaker baker;
+
+ memset(&baker, 0, sizeof(baker));
+ baker.main = bmain;
+ baker.scene = scene;
+ baker.bake = 0;
+ baker.render = 0;
+ baker.anim_init = 0;
+ baker.quick_step = scene->physics_settings.quick_cache_step;
+
+ BKE_ptcache_bake(&baker);
+}
+
+static void ptcache_dt_to_str(char *str, double dtime)
+{
+ if (dtime > 60.0) {
+ if (dtime > 3600.0)
+ sprintf(str, "%ih %im %is", (int)(dtime/3600), ((int)(dtime/60))%60, ((int)dtime) % 60);
+ else
+ sprintf(str, "%im %is", ((int)(dtime/60))%60, ((int)dtime) % 60);
+ }
+ else
+ sprintf(str, "%is", ((int)dtime) % 60);
+}
+
+/* if bake is not given run simulations to current frame */
+void BKE_ptcache_bake(PTCacheBaker *baker)
+{
+ Main *bmain = baker->main;
+ Scene *scene = baker->scene;
+ Scene *sce_iter; /* SETLOOPER macro only */
+ Base *base;
+ ListBase pidlist;
+ PTCacheID *pid = &baker->pid;
+ PointCache *cache = NULL;
+ float frameleno = scene->r.framelen;
+ int cfrao = CFRA;
+ int startframe = MAXFRAME, endframe = baker->anim_init ? scene->r.sfra : CFRA;
+ int bake = baker->bake;
+ int render = baker->render;
+
+ G.is_break = false;
+
+ /* set caches to baking mode and figure out start frame */
+ if (pid->ob) {
+ /* cache/bake a single object */
+ cache = pid->cache;
+ if ((cache->flag & PTCACHE_BAKED)==0) {
+ if (pid->type==PTCACHE_TYPE_PARTICLES) {
+ ParticleSystem *psys= pid->calldata;
+
+ /* a bit confusing, could make this work better in the UI */
+ if (psys->part->type == PART_EMITTER)
+ psys_get_pointcache_start_end(scene, pid->calldata, &cache->startframe, &cache->endframe);
+ }
+ else if (pid->type == PTCACHE_TYPE_SMOKE_HIGHRES) {
+ /* get all pids from the object and search for smoke low res */
+ ListBase pidlist2;
+ PTCacheID *pid2;
+ BKE_ptcache_ids_from_object(&pidlist2, pid->ob, scene, MAX_DUPLI_RECUR);
+ for (pid2=pidlist2.first; pid2; pid2=pid2->next) {
+ if (pid2->type == PTCACHE_TYPE_SMOKE_DOMAIN) {
+ if (pid2->cache && !(pid2->cache->flag & PTCACHE_BAKED)) {
+ if (bake || pid2->cache->flag & PTCACHE_REDO_NEEDED)
+ BKE_ptcache_id_clear(pid2, PTCACHE_CLEAR_ALL, 0);
+ if (bake) {
+ pid2->cache->flag |= PTCACHE_BAKING;
+ pid2->cache->flag &= ~PTCACHE_BAKED;
+ }
+ }
+ }
+ }
+ BLI_freelistN(&pidlist2);
+ }
+
+ if (bake || cache->flag & PTCACHE_REDO_NEEDED)
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+
+ startframe = MAX2(cache->last_exact, cache->startframe);
+
+ if (bake) {
+ endframe = cache->endframe;
+ cache->flag |= PTCACHE_BAKING;
+ }
+ else {
+ endframe = MIN2(endframe, cache->endframe);
+ }
+
+ cache->flag &= ~PTCACHE_BAKED;
+ }
+ }
+ else {
+ for (SETLOOPER(scene, sce_iter, base)) {
+ /* cache/bake everything in the scene */
+ BKE_ptcache_ids_from_object(&pidlist, base->object, scene, MAX_DUPLI_RECUR);
+
+ for (pid=pidlist.first; pid; pid=pid->next) {
+ cache = pid->cache;
+ if ((cache->flag & PTCACHE_BAKED)==0) {
+ if (pid->type==PTCACHE_TYPE_PARTICLES) {
+ ParticleSystem *psys = (ParticleSystem*)pid->calldata;
+ /* skip hair & keyed particles */
+ if (psys->part->type == PART_HAIR || psys->part->phystype == PART_PHYS_KEYED)
+ continue;
+
+ psys_get_pointcache_start_end(scene, pid->calldata, &cache->startframe, &cache->endframe);
+ }
+
+ if ((cache->flag & PTCACHE_REDO_NEEDED || (cache->flag & PTCACHE_SIMULATION_VALID)==0) &&
+ (render || bake))
+ {
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+ }
+
+ startframe = MIN2(startframe, cache->startframe);
+
+ if (bake || render) {
+ cache->flag |= PTCACHE_BAKING;
+
+ if (bake)
+ endframe = MAX2(endframe, cache->endframe);
+ }
+
+ cache->flag &= ~PTCACHE_BAKED;
+
+ }
+ }
+ BLI_freelistN(&pidlist);
+ }
+ }
+
+ CFRA = startframe;
+ scene->r.framelen = 1.0;
+
+ /* bake */
+
+ bool use_timer = false;
+ double stime, ptime, ctime, fetd;
+ char run[32], cur[32], etd[32];
+ int cancel = 0;
+
+ stime = ptime = PIL_check_seconds_timer();
+
+ for (int fr = CFRA; fr <= endframe; fr += baker->quick_step, CFRA = fr) {
+ BKE_scene_update_for_newframe(G.main->eval_ctx, bmain, scene, scene->lay);
+
+ if (baker->update_progress) {
+ float progress = ((float)(CFRA - startframe)/(float)(endframe - startframe));
+ baker->update_progress(baker->bake_job, progress, &cancel);
+ }
+
+ if (G.background) {
+ printf("bake: frame %d :: %d\n", CFRA, endframe);
+ }
+ else {
+ ctime = PIL_check_seconds_timer();
+
+ fetd = (ctime - ptime) * (endframe - CFRA) / baker->quick_step;
+
+ if (use_timer || fetd > 60.0) {
+ use_timer = true;
+
+ ptcache_dt_to_str(cur, ctime - ptime);
+ ptcache_dt_to_str(run, ctime - stime);
+ ptcache_dt_to_str(etd, fetd);
+
+ printf("Baked for %s, current frame: %i/%i (%.3fs), ETC: %s\r",
+ run, CFRA - startframe + 1, endframe - startframe + 1, ctime - ptime, etd);
+ }
+
+ ptime = ctime;
+ }
+
+ /* NOTE: breaking baking should leave calculated frames in cache, not clear it */
+ if ((cancel || G.is_break)) {
+ break;
+ }
+
+ CFRA += 1;
+ }
+
+ if (use_timer) {
+ /* start with newline because of \r above */
+ ptcache_dt_to_str(run, PIL_check_seconds_timer()-stime);
+ printf("\nBake %s %s (%i frames simulated).\n", (cancel ? "canceled after" : "finished in"), run, CFRA - startframe);
+ }
+
+ /* clear baking flag */
+ if (pid) {
+ cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED);
+ cache->flag |= PTCACHE_SIMULATION_VALID;
+ if (bake) {
+ cache->flag |= PTCACHE_BAKED;
+ /* write info file */
+ if (cache->flag & PTCACHE_DISK_CACHE)
+ BKE_ptcache_write(pid, 0);
+ }
+ }
+ else {
+ for (SETLOOPER(scene, sce_iter, base)) {
+ BKE_ptcache_ids_from_object(&pidlist, base->object, scene, MAX_DUPLI_RECUR);
+
+ for (pid=pidlist.first; pid; pid=pid->next) {
+ /* skip hair particles */
+ if (pid->type==PTCACHE_TYPE_PARTICLES && ((ParticleSystem*)pid->calldata)->part->type == PART_HAIR)
+ continue;
+
+ cache = pid->cache;
+
+ if (baker->quick_step > 1)
+ cache->flag &= ~(PTCACHE_BAKING|PTCACHE_OUTDATED);
+ else
+ cache->flag &= ~(PTCACHE_BAKING|PTCACHE_REDO_NEEDED);
+
+ cache->flag |= PTCACHE_SIMULATION_VALID;
+
+ if (bake) {
+ cache->flag |= PTCACHE_BAKED;
+ if (cache->flag & PTCACHE_DISK_CACHE)
+ BKE_ptcache_write(pid, 0);
+ }
+ }
+ BLI_freelistN(&pidlist);
+ }
+ }
+
+ scene->r.framelen = frameleno;
+ CFRA = cfrao;
+
+ if (bake) { /* already on cfra unless baking */
+ BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay);
+ }
+
+ /* TODO: call redraw all windows somehow */
+}
+/* Helpers */
+void BKE_ptcache_disk_to_mem(PTCacheID *pid)
+{
+ PointCache *cache = pid->cache;
+ PTCacheMem *pm = NULL;
+ int baked = cache->flag & PTCACHE_BAKED;
+ int cfra, sfra = cache->startframe, efra = cache->endframe;
+
+ /* Remove possible bake flag to allow clear */
+ cache->flag &= ~PTCACHE_BAKED;
+
+ /* PTCACHE_DISK_CACHE flag was cleared already */
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+
+ /* restore possible bake flag */
+ cache->flag |= baked;
+
+ for (cfra=sfra; cfra <= efra; cfra++) {
+ pm = ptcache_disk_frame_to_mem(pid, cfra);
+
+ if (pm)
+ BLI_addtail(&pid->cache->mem_cache, pm);
+ }
+}
+void BKE_ptcache_mem_to_disk(PTCacheID *pid)
+{
+ PointCache *cache = pid->cache;
+ PTCacheMem *pm = cache->mem_cache.first;
+ int baked = cache->flag & PTCACHE_BAKED;
+
+ /* Remove possible bake flag to allow clear */
+ cache->flag &= ~PTCACHE_BAKED;
+
+ /* PTCACHE_DISK_CACHE flag was set already */
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+
+ /* restore possible bake flag */
+ cache->flag |= baked;
+
+ for (; pm; pm=pm->next) {
+ if (ptcache_mem_frame_to_disk(pid, pm)==0) {
+ cache->flag &= ~PTCACHE_DISK_CACHE;
+ break;
+ }
+ }
+
+ /* write info file */
+ if (cache->flag & PTCACHE_BAKED)
+ BKE_ptcache_write(pid, 0);
+}
+void BKE_ptcache_toggle_disk_cache(PTCacheID *pid)
+{
+ PointCache *cache = pid->cache;
+ int last_exact = cache->last_exact;
+
+ if (!G.relbase_valid) {
+ cache->flag &= ~PTCACHE_DISK_CACHE;
+ if (G.debug & G_DEBUG)
+ printf("File must be saved before using disk cache!\n");
+ return;
+ }
+
+ if (cache->cached_frames) {
+ MEM_freeN(cache->cached_frames);
+ cache->cached_frames=NULL;
+ }
+
+ if (cache->flag & PTCACHE_DISK_CACHE)
+ BKE_ptcache_mem_to_disk(pid);
+ else
+ BKE_ptcache_disk_to_mem(pid);
+
+ cache->flag ^= PTCACHE_DISK_CACHE;
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+ cache->flag ^= PTCACHE_DISK_CACHE;
+
+ cache->last_exact = last_exact;
+
+ BKE_ptcache_id_time(pid, NULL, 0.0f, NULL, NULL, NULL);
+
+ BKE_ptcache_update_info(pid);
+
+ if ((cache->flag & PTCACHE_DISK_CACHE) == 0) {
+ if (cache->index) {
+ BKE_object_delete_ptcache(pid->ob, cache->index);
+ cache->index = -1;
+ }
+ }
+}
+
+void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const char *name_dst)
+{
+ char old_name[80];
+ int len; /* store the length of the string */
+ /* mode is same as fopen's modes */
+ DIR *dir;
+ struct dirent *de;
+ char path[MAX_PTCACHE_PATH];
+ char old_filename[MAX_PTCACHE_FILE];
+ char new_path_full[MAX_PTCACHE_FILE];
+ char old_path_full[MAX_PTCACHE_FILE];
+ char ext[MAX_PTCACHE_PATH];
+
+ /* save old name */
+ BLI_strncpy(old_name, pid->cache->name, sizeof(old_name));
+
+ /* get "from" filename */
+ BLI_strncpy(pid->cache->name, name_src, sizeof(pid->cache->name));
+
+ len = ptcache_filename(pid, old_filename, 0, 0, 0); /* no path */
+
+ ptcache_path(pid, path);
+ dir = opendir(path);
+ if (dir==NULL) {
+ BLI_strncpy(pid->cache->name, old_name, sizeof(pid->cache->name));
+ return;
+ }
+
+ const char *fext = ptcache_file_extension(pid);
+
+ BLI_snprintf(ext, sizeof(ext), "_%02u%s", pid->stack_index, fext);
+
+ /* put new name into cache */
+ BLI_strncpy(pid->cache->name, name_dst, sizeof(pid->cache->name));
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strstr(de->d_name, ext)) { /* do we have the right extension?*/
+ if (STREQLEN(old_filename, de->d_name, len)) { /* do we have the right prefix */
+ /* read the number of the file */
+ const int frame = ptcache_frame_from_filename(de->d_name, ext);
+
+ if (frame != -1) {
+ BLI_join_dirfile(old_path_full, sizeof(old_path_full), path, de->d_name);
+ ptcache_filename(pid, new_path_full, frame, 1, 1);
+ BLI_rename(old_path_full, new_path_full);
+ }
+ }
+ }
+ }
+ closedir(dir);
+
+ BLI_strncpy(pid->cache->name, old_name, sizeof(pid->cache->name));
+}
+
+void BKE_ptcache_load_external(PTCacheID *pid)
+{
+ /*todo*/
+ PointCache *cache = pid->cache;
+ int len; /* store the length of the string */
+ int info = 0;
+ int start = MAXFRAME;
+ int end = -1;
+
+ /* mode is same as fopen's modes */
+ DIR *dir;
+ struct dirent *de;
+ char path[MAX_PTCACHE_PATH];
+ char filename[MAX_PTCACHE_FILE];
+ char ext[MAX_PTCACHE_PATH];
+
+ if (!cache)
+ return;
+
+ ptcache_path(pid, path);
+
+ len = ptcache_filename(pid, filename, 1, 0, 0); /* no path */
+
+ dir = opendir(path);
+ if (dir==NULL)
+ return;
+
+ const char *fext = ptcache_file_extension(pid);
+
+ if (cache->index >= 0)
+ BLI_snprintf(ext, sizeof(ext), "_%02d%s", cache->index, fext);
+ else
+ BLI_strncpy(ext, fext, sizeof(ext));
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strstr(de->d_name, ext)) { /* do we have the right extension?*/
+ if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */
+ /* read the number of the file */
+ const int frame = ptcache_frame_from_filename(de->d_name, ext);
+
+ if (frame != -1) {
+ if (frame) {
+ start = MIN2(start, frame);
+ end = MAX2(end, frame);
+ }
+ else
+ info = 1;
+ }
+ }
+ }
+ }
+ closedir(dir);
+
+ if (start != MAXFRAME) {
+ PTCacheFile *pf;
+
+ cache->startframe = start;
+ cache->endframe = end;
+ cache->totpoint = 0;
+
+ if (pid->type == PTCACHE_TYPE_SMOKE_DOMAIN) {
+ /* necessary info in every file */
+ }
+ /* read totpoint from info file (frame 0) */
+ else if (info) {
+ pf= ptcache_file_open(pid, PTCACHE_FILE_READ, 0);
+
+ if (pf) {
+ if (ptcache_file_header_begin_read(pf)) {
+ if (pf->type == pid->type && pid->read_header(pf)) {
+ cache->totpoint = pf->totpoint;
+ cache->flag |= PTCACHE_READ_INFO;
+ }
+ else {
+ cache->totpoint = 0;
+ }
+ }
+ ptcache_file_close(pf);
+ }
+ }
+ /* or from any old format cache file */
+ else {
+ float old_data[14];
+ int elemsize = ptcache_old_elemsize(pid);
+ pf= ptcache_file_open(pid, PTCACHE_FILE_READ, cache->startframe);
+
+ if (pf) {
+ while (ptcache_file_read(pf, old_data, 1, elemsize))
+ cache->totpoint++;
+
+ ptcache_file_close(pf);
+ }
+ }
+ cache->flag |= (PTCACHE_BAKED|PTCACHE_DISK_CACHE|PTCACHE_SIMULATION_VALID);
+ cache->flag &= ~(PTCACHE_OUTDATED|PTCACHE_FRAMES_SKIPPED);
+ }
+
+ /* make sure all new frames are loaded */
+ if (cache->cached_frames) {
+ MEM_freeN(cache->cached_frames);
+ cache->cached_frames=NULL;
+ }
+ BKE_ptcache_update_info(pid);
+}
+
+void BKE_ptcache_update_info(PTCacheID *pid)
+{
+ PointCache *cache = pid->cache;
+ PTCacheExtra *extra = NULL;
+ int totframes = 0;
+ char mem_info[64];
+
+ if (cache->flag & PTCACHE_EXTERNAL) {
+ int cfra = cache->startframe;
+
+ for (; cfra <= cache->endframe; cfra++) {
+ if (BKE_ptcache_id_exist(pid, cfra))
+ totframes++;
+ }
+
+ /* smoke doesn't use frame 0 as info frame so can't check based on totpoint */
+ if (pid->type == PTCACHE_TYPE_SMOKE_DOMAIN && totframes)
+ BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%i frames found!"), totframes);
+ else if (totframes && cache->totpoint)
+ BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%i points found!"), cache->totpoint);
+ else
+ BLI_strncpy(cache->info, IFACE_("No valid data to read!"), sizeof(cache->info));
+ return;
+ }
+
+ if (cache->flag & PTCACHE_DISK_CACHE) {
+ if (pid->type == PTCACHE_TYPE_SMOKE_DOMAIN) {
+ int totpoint = pid->totpoint(pid->calldata, 0);
+
+ if (cache->totpoint > totpoint)
+ BLI_snprintf(mem_info, sizeof(mem_info), IFACE_("%i cells + High Resolution cached"), totpoint);
+ else
+ BLI_snprintf(mem_info, sizeof(mem_info), IFACE_("%i cells cached"), totpoint);
+ }
+ else {
+ int cfra = cache->startframe;
+
+ for (; cfra <= cache->endframe; cfra++) {
+ if (BKE_ptcache_id_exist(pid, cfra))
+ totframes++;
+ }
+
+ BLI_snprintf(mem_info, sizeof(mem_info), IFACE_("%i frames on disk"), totframes);
+ }
+ }
+ else {
+ PTCacheMem *pm = cache->mem_cache.first;
+ float bytes = 0.0f;
+ int i, mb;
+
+ for (; pm; pm=pm->next) {
+ for (i=0; i<BPHYS_TOT_DATA; i++)
+ bytes += MEM_allocN_len(pm->data[i]);
+
+ for (extra=pm->extradata.first; extra; extra=extra->next) {
+ bytes += MEM_allocN_len(extra->data);
+ bytes += sizeof(PTCacheExtra);
+ }
+
+ bytes += sizeof(PTCacheMem);
+
+ totframes++;
+ }
+
+ mb = (bytes > 1024.0f * 1024.0f);
+
+ BLI_snprintf(mem_info, sizeof(mem_info), IFACE_("%i frames in memory (%.1f %s)"),
+ totframes,
+ bytes / (mb ? 1024.0f * 1024.0f : 1024.0f),
+ mb ? IFACE_("Mb") : IFACE_("kb"));
+ }
+
+ if (cache->flag & PTCACHE_OUTDATED) {
+ BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%s, cache is outdated!"), mem_info);
+ }
+ else if (cache->flag & PTCACHE_FRAMES_SKIPPED) {
+ BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%s, not exact since frame %i"),
+ mem_info, cache->last_exact);
+ }
+ else {
+ BLI_snprintf(cache->info, sizeof(cache->info), "%s.", mem_info);
+ }
+}
+
+void BKE_ptcache_validate(PointCache *cache, int framenr)
+{
+ if (cache) {
+ cache->flag |= PTCACHE_SIMULATION_VALID;
+ cache->simframe = framenr;
+ }
+}
+void BKE_ptcache_invalidate(PointCache *cache)
+{
+ if (cache) {
+ cache->flag &= ~PTCACHE_SIMULATION_VALID;
+ cache->simframe = 0;
+ cache->last_exact = MIN2(cache->startframe, 0);
+ }
+}
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 7c6dceeb821..6f86c68dc07 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -62,6 +62,7 @@
#include "BKE_library_query.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_pointcache.h"
#include "BKE_rigidbody.h"
#include "BKE_scene.h"
@@ -117,6 +118,10 @@ void BKE_rigidbody_free_world(RigidBodyWorld *rbw)
if (rbw->objects)
free(rbw->objects);
+ /* free cache */
+ BKE_ptcache_free_list(&(rbw->ptcaches));
+ rbw->pointcache = NULL;
+
/* free effector weights */
if (rbw->effector_weights)
MEM_freeN(rbw->effector_weights);
@@ -933,6 +938,9 @@ RigidBodyWorld *BKE_rigidbody_create_world(Scene *scene)
rbw->steps_per_second = 60; /* Bullet default (60 Hz) */
rbw->num_solver_iterations = 10; /* 10 is bullet default */
+ rbw->pointcache = BKE_ptcache_add(&(rbw->ptcaches));
+ rbw->pointcache->step = 1;
+
/* return this sim world */
return rbw;
}
@@ -948,6 +956,8 @@ RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw)
if (rbwn->constraints)
id_us_plus(&rbwn->constraints->id);
+ rbwn->pointcache = BKE_ptcache_copy_list(&rbwn->ptcaches, &rbw->ptcaches, false);
+
rbwn->objects = NULL;
rbwn->physics_world = NULL;
rbwn->numbodies = 0;
@@ -977,9 +987,10 @@ void BKE_rigidbody_world_id_loop(RigidBodyWorld *rbw, RigidbodyWorldIDFunc func,
}
/* Add rigid body settings to the specified object */
-RigidBodyOb *BKE_rigidbody_create_object(Scene *UNUSED(scene), Object *ob, short type)
+RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type)
{
RigidBodyOb *rbo;
+ RigidBodyWorld *rbw = scene->rigidbody_world;
/* sanity checks
* - rigidbody world must exist
@@ -1023,14 +1034,18 @@ RigidBodyOb *BKE_rigidbody_create_object(Scene *UNUSED(scene), Object *ob, short
/* set initial transform */
mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat);
+ /* flag cache as outdated */
+ BKE_rigidbody_cache_reset(rbw);
+
/* return this object */
return rbo;
}
/* Add rigid body constraint to the specified object */
-RigidBodyCon *BKE_rigidbody_create_constraint(Scene *UNUSED(scene), Object *ob, short type)
+RigidBodyCon *BKE_rigidbody_create_constraint(Scene *scene, Object *ob, short type)
{
RigidBodyCon *rbc;
+ RigidBodyWorld *rbw = scene->rigidbody_world;
/* sanity checks
* - rigidbody world must exist
@@ -1086,6 +1101,9 @@ RigidBodyCon *BKE_rigidbody_create_constraint(Scene *UNUSED(scene), Object *ob,
rbc->motor_ang_max_impulse = 1.0f;
rbc->motor_ang_target_velocity = 1.0f;
+ /* flag cache as outdated */
+ BKE_rigidbody_cache_reset(rbw);
+
/* return this object */
return rbc;
}
@@ -1145,6 +1163,9 @@ void BKE_rigidbody_remove_object(Scene *scene, Object *ob)
/* remove object's settings */
BKE_rigidbody_free_object(ob);
+
+ /* flag cache as outdated */
+ BKE_rigidbody_cache_reset(rbw);
}
void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob)
@@ -1158,6 +1179,9 @@ void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob)
}
/* remove object's settings */
BKE_rigidbody_free_constraint(ob);
+
+ /* flag cache as outdated */
+ BKE_rigidbody_cache_reset(rbw);
}
@@ -1251,7 +1275,7 @@ static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *o
ListBase *effectors;
/* get effectors present in the group specified by effector_weights */
- effectors = pdInitEffectors(scene, ob, effector_weights, true);
+ effectors = pdInitEffectors(scene, ob, NULL, effector_weights, true);
if (effectors) {
float eff_force[3] = {0.0f, 0.0f, 0.0f};
float eff_loc[3], eff_vel[3];
@@ -1424,9 +1448,9 @@ static void rigidbody_update_simulation_post_step(RigidBodyWorld *rbw)
}
}
-bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float UNUSED(ctime))
+bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime)
{
- return (rbw && (rbw->flag & RBW_FLAG_MUTED) == 0);
+ return (rbw && (rbw->flag & RBW_FLAG_MUTED) == 0 && ctime > rbw->pointcache->startframe);
}
/* Sync rigid body and object transformations */
@@ -1489,6 +1513,12 @@ void BKE_rigidbody_aftertrans_update(Object *ob, float loc[3], float rot[3], flo
// RB_TODO update rigid body physics object's loc/rot for dynamic objects here as well (needs to be done outside bullet's update loop)
}
+void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
+{
+ if (rbw)
+ rbw->pointcache->flag |= PTCACHE_OUTDATED;
+}
+
/* ------------------ */
/* Rebuild rigid body world */
@@ -1496,10 +1526,27 @@ void BKE_rigidbody_aftertrans_update(Object *ob, float loc[3], float rot[3], flo
void BKE_rigidbody_rebuild_world(Scene *scene, float ctime)
{
RigidBodyWorld *rbw = scene->rigidbody_world;
- int startframe = scene->r.sfra;
+ PointCache *cache;
+ PTCacheID pid;
+ int startframe, endframe;
+
+ BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw);
+ BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL);
+ cache = rbw->pointcache;
+
+ /* flag cache as outdated if we don't have a world or number of objects in the simulation has changed */
+ if (rbw->physics_world == NULL || rbw->numbodies != BLI_listbase_count(&rbw->group->gobject)) {
+ cache->flag |= PTCACHE_OUTDATED;
+ }
if (ctime == startframe + 1 && rbw->ltime == startframe) {
- rigidbody_update_simulation(scene, rbw, true);
+ if (cache->flag & PTCACHE_OUTDATED) {
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
+ rigidbody_update_simulation(scene, rbw, true);
+ BKE_ptcache_validate(cache, (int)ctime);
+ cache->last_exact = 0;
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+ }
}
}
@@ -1508,7 +1555,13 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
{
float timestep;
RigidBodyWorld *rbw = scene->rigidbody_world;
- int startframe = scene->r.sfra, endframe = scene->r.efra;
+ PointCache *cache;
+ PTCacheID pid;
+ int startframe, endframe;
+
+ BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw);
+ BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL);
+ cache = rbw->pointcache;
if (ctime <= startframe) {
rbw->ltime = startframe;
@@ -1519,14 +1572,29 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
ctime = endframe;
}
- /* don't try to run the simulation if we don't have a world yet */
- if (rbw->physics_world == NULL)
+ /* don't try to run the simulation if we don't have a world yet but allow reading baked cache */
+ if (rbw->physics_world == NULL && !(cache->flag & PTCACHE_BAKED))
return;
else if (rbw->objects == NULL)
rigidbody_update_ob_array(rbw);
+ /* try to read from cache */
+ // RB_TODO deal with interpolated, old and baked results
+ bool can_simulate = (ctime == rbw->ltime + 1) && !(cache->flag & PTCACHE_BAKED);
+
+ if (BKE_ptcache_read(&pid, ctime, can_simulate)) {
+ BKE_ptcache_validate(cache, (int)ctime);
+ rbw->ltime = ctime;
+ return;
+ }
+
/* advance simulation, we can only step one frame forward */
if (ctime == rbw->ltime + 1) {
+ /* write cache for first frame when on second frame */
+ if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
+ BKE_ptcache_write(&pid, startframe);
+ }
+
/* update and validate simulation */
rigidbody_update_simulation(scene, rbw, false);
@@ -1537,6 +1605,10 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
rigidbody_update_simulation_post_step(rbw);
+ /* write cache for current frame */
+ BKE_ptcache_validate(cache, (int)ctime);
+ BKE_ptcache_write(&pid, (unsigned int)ctime);
+
rbw->ltime = ctime;
}
}
@@ -1567,6 +1639,7 @@ void BKE_rigidbody_remove_constraint(Scene *scene, Object *ob) {}
void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) {}
void BKE_rigidbody_aftertrans_update(Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle) {}
bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime) { return false; }
+void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw) {}
void BKE_rigidbody_rebuild_world(Scene *scene, float ctime) {}
void BKE_rigidbody_do_simulation(Scene *scene, float ctime) {}
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index cab0a876292..091b8100d27 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -285,6 +285,7 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type)
BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint);
ts->imapaint.paintcursor = NULL;
id_us_plus((ID *)ts->imapaint.stencil);
+ ts->particle.paintcursor = NULL;
/* duplicate Grease Pencil Drawing Brushes */
BLI_listbase_clear(&ts->gp_brushes);
for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) {
@@ -465,6 +466,8 @@ void BKE_scene_free(Scene *sce)
void BKE_scene_init(Scene *sce)
{
+ ParticleEditSettings *pset;
+ int a;
const char *colorspace_name;
SceneRenderView *srv;
CurveMapping *mblur_shutter_curve;
@@ -640,6 +643,23 @@ void BKE_scene_init(Scene *sce)
sce->unit.scale_length = 1.0f;
+ pset = &sce->toolsettings->particle;
+ pset->flag = PE_KEEP_LENGTHS | PE_LOCK_FIRST | PE_DEFLECT_EMITTER | PE_AUTO_VELOCITY;
+ pset->emitterdist = 0.25f;
+ pset->totrekey = 5;
+ pset->totaddkey = 5;
+ pset->brushtype = PE_BRUSH_NONE;
+ pset->draw_step = 2;
+ pset->fade_frames = 2;
+ pset->selectmode = SCE_SELECT_PATH;
+ for (a = 0; a < PE_TOT_BRUSH; a++) {
+ pset->brush[a].strength = 0.5f;
+ pset->brush[a].size = 50;
+ pset->brush[a].step = 10;
+ pset->brush[a].count = 10;
+ }
+ pset->brush[PE_BRUSH_CUT].strength = 1.0f;
+
sce->r.ffcodecdata.audio_mixrate = 48000;
sce->r.ffcodecdata.audio_volume = 1.0f;
sce->r.ffcodecdata.audio_bitrate = 192;
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index 1da263797f6..e8970d416e9 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -56,8 +56,8 @@
#include "DNA_lamp_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
-#include "DNA_object_force.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_smoke_types.h"
@@ -77,6 +77,8 @@
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_smoke.h"
#include "BKE_texture.h"
@@ -355,6 +357,9 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd)
MEM_freeN(smd->domain->effector_weights);
smd->domain->effector_weights = NULL;
+ BKE_ptcache_free_list(&(smd->domain->ptcaches[0]));
+ smd->domain->point_cache[0] = NULL;
+
if (smd->domain->coba) {
MEM_freeN(smd->domain->coba);
}
@@ -483,6 +488,13 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
smd->domain->smd = smd;
+ smd->domain->point_cache[0] = BKE_ptcache_add(&(smd->domain->ptcaches[0]));
+ smd->domain->point_cache[0]->flag |= PTCACHE_DISK_CACHE;
+ smd->domain->point_cache[0]->step = 1;
+
+ /* Deprecated */
+ smd->domain->point_cache[1] = NULL;
+ BLI_listbase_clear(&smd->domain->ptcaches[1]);
/* set some standard values */
smd->domain->fluid = NULL;
smd->domain->fluid_mutex = BLI_rw_mutex_alloc();
@@ -527,6 +539,7 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
smd->domain->openvdb_comp = VDB_COMPRESSION_ZIP;
#endif
smd->domain->data_depth = 0;
+ smd->domain->cache_file_format = PTCACHE_FILE_PTCACHE;
smd->domain->display_thickness = 1.0f;
smd->domain->slice_method = MOD_SMOKE_SLICE_VIEW_ALIGNED;
@@ -566,6 +579,8 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
smd->flow->color[2] = 0.7f;
smd->flow->dm = NULL;
+ smd->flow->psys = NULL;
+
}
else if (smd->type & MOD_SMOKE_TYPE_COLL)
{
@@ -644,6 +659,7 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData
}
}
else if (tsmd->flow) {
+ tsmd->flow->psys = smd->flow->psys;
tsmd->flow->noise_texture = smd->flow->noise_texture;
tsmd->flow->vel_multi = smd->flow->vel_multi;
@@ -1153,6 +1169,248 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult
em_freeData(&em1);
}
+typedef struct EmitFromParticlesData {
+ SmokeFlowSettings *sfs;
+ KDTree *tree;
+ int hires_multiplier;
+
+ EmissionMap *em;
+ float *particle_vel;
+ float hr;
+
+ int *min, *max, *res;
+
+ float solid;
+ float smooth;
+ float hr_smooth;
+} EmitFromParticlesData;
+
+static void emit_from_particles_task_cb(void *userdata, const int z)
+{
+ EmitFromParticlesData *data = userdata;
+ SmokeFlowSettings *sfs = data->sfs;
+ EmissionMap *em = data->em;
+ const int hires_multiplier = data->hires_multiplier;
+
+ for (int x = data->min[0]; x < data->max[0]; x++) {
+ for (int y = data->min[1]; y < data->max[1]; y++) {
+ /* take low res samples where possible */
+ if (hires_multiplier <= 1 || !(x % hires_multiplier || y % hires_multiplier || z % hires_multiplier)) {
+ /* get low res space coordinates */
+ const int lx = x / hires_multiplier;
+ const int ly = y / hires_multiplier;
+ const int lz = z / hires_multiplier;
+
+ const int index = smoke_get_index(lx - em->min[0], em->res[0], ly - em->min[1], em->res[1], lz - em->min[2]);
+ const float ray_start[3] = {((float)lx) + 0.5f, ((float)ly) + 0.5f, ((float)lz) + 0.5f};
+
+ /* find particle distance from the kdtree */
+ KDTreeNearest nearest;
+ const float range = data->solid + data->smooth;
+ BLI_kdtree_find_nearest(data->tree, ray_start, &nearest);
+
+ if (nearest.dist < range) {
+ em->influence[index] = (nearest.dist < data->solid) ?
+ 1.0f : (1.0f - (nearest.dist - data->solid) / data->smooth);
+ /* Uses particle velocity as initial velocity for smoke */
+ if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY && (sfs->psys->part->phystype != PART_PHYS_NO)) {
+ VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3],
+ &data->particle_vel[nearest.index * 3], sfs->vel_multi);
+ }
+ }
+ }
+
+ /* take high res samples if required */
+ if (hires_multiplier > 1) {
+ /* get low res space coordinates */
+ const float lx = ((float)x) * data->hr;
+ const float ly = ((float)y) * data->hr;
+ const float lz = ((float)z) * data->hr;
+
+ const int index = smoke_get_index(
+ x - data->min[0], data->res[0], y - data->min[1], data->res[1], z - data->min[2]);
+ const float ray_start[3] = {lx + 0.5f * data->hr, ly + 0.5f * data->hr, lz + 0.5f * data->hr};
+
+ /* find particle distance from the kdtree */
+ KDTreeNearest nearest;
+ const float range = data->solid + data->hr_smooth;
+ BLI_kdtree_find_nearest(data->tree, ray_start, &nearest);
+
+ if (nearest.dist < range) {
+ em->influence_high[index] = (nearest.dist < data->solid) ?
+ 1.0f : (1.0f - (nearest.dist - data->solid) / data->smooth);
+ }
+ }
+
+ }
+ }
+}
+
+static void emit_from_particles(
+ Object *flow_ob, SmokeDomainSettings *sds, SmokeFlowSettings *sfs, EmissionMap *em, Scene *scene, float dt)
+{
+ if (sfs && sfs->psys && sfs->psys->part && ELEM(sfs->psys->part->type, PART_EMITTER, PART_FLUID)) // is particle system selected
+ {
+ ParticleSimulationData sim;
+ ParticleSystem *psys = sfs->psys;
+ float *particle_pos;
+ float *particle_vel;
+ int totpart = psys->totpart, totchild;
+ int p = 0;
+ int valid_particles = 0;
+ int bounds_margin = 1;
+
+ /* radius based flow */
+ const float solid = sfs->particle_size * 0.5f;
+ const float smooth = 0.5f; /* add 0.5 cells of linear falloff to reduce aliasing */
+ int hires_multiplier = 1;
+ KDTree *tree = NULL;
+
+ sim.scene = scene;
+ sim.ob = flow_ob;
+ sim.psys = psys;
+
+ /* prepare curvemapping tables */
+ if ((psys->part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && psys->part->clumpcurve)
+ curvemapping_changed_all(psys->part->clumpcurve);
+ if ((psys->part->child_flag & PART_CHILD_USE_ROUGH_CURVE) && psys->part->roughcurve)
+ curvemapping_changed_all(psys->part->roughcurve);
+
+ /* initialize particle cache */
+ if (psys->part->type == PART_HAIR) {
+ // TODO: PART_HAIR not supported whatsoever
+ totchild = 0;
+ }
+ else {
+ totchild = psys->totchild * psys->part->disp / 100;
+ }
+
+ particle_pos = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles");
+ particle_vel = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles");
+
+ /* setup particle radius emission if enabled */
+ if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE) {
+ tree = BLI_kdtree_new(psys->totpart + psys->totchild);
+
+ /* check need for high resolution map */
+ if ((sds->flags & MOD_SMOKE_HIGHRES) && (sds->highres_sampling == SM_HRES_FULLSAMPLE)) {
+ hires_multiplier = sds->amplify + 1;
+ }
+
+ bounds_margin = (int)ceil(solid + smooth);
+ }
+
+ /* calculate local position for each particle */
+ for (p = 0; p < totpart + totchild; p++)
+ {
+ ParticleKey state;
+ float *pos;
+ if (p < totpart) {
+ if (psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST))
+ continue;
+ }
+ else {
+ /* handle child particle */
+ ChildParticle *cpa = &psys->child[p - totpart];
+ if (psys->particles[cpa->parent].flag & (PARS_NO_DISP | PARS_UNEXIST))
+ continue;
+ }
+
+ state.time = BKE_scene_frame_get(scene); /* use scene time */
+ if (psys_get_particle_state(&sim, p, &state, 0) == 0)
+ continue;
+
+ /* location */
+ pos = &particle_pos[valid_particles * 3];
+ copy_v3_v3(pos, state.co);
+ smoke_pos_to_cell(sds, pos);
+
+ /* velocity */
+ copy_v3_v3(&particle_vel[valid_particles * 3], state.vel);
+ mul_mat3_m4_v3(sds->imat, &particle_vel[valid_particles * 3]);
+
+ if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE) {
+ BLI_kdtree_insert(tree, valid_particles, pos);
+ }
+
+ /* calculate emission map bounds */
+ em_boundInsert(em, pos);
+ valid_particles++;
+ }
+
+ /* set emission map */
+ clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, bounds_margin, dt);
+ em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier);
+
+ if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) {
+ for (p = 0; p < valid_particles; p++)
+ {
+ int cell[3];
+ size_t i = 0;
+ size_t index = 0;
+ int badcell = 0;
+
+ /* 1. get corresponding cell */
+ cell[0] = floor(particle_pos[p * 3]) - em->min[0];
+ cell[1] = floor(particle_pos[p * 3 + 1]) - em->min[1];
+ cell[2] = floor(particle_pos[p * 3 + 2]) - em->min[2];
+ /* check if cell is valid (in the domain boundary) */
+ for (i = 0; i < 3; i++) {
+ if ((cell[i] > em->res[i] - 1) || (cell[i] < 0)) {
+ badcell = 1;
+ break;
+ }
+ }
+ if (badcell)
+ continue;
+ /* get cell index */
+ index = smoke_get_index(cell[0], em->res[0], cell[1], em->res[1], cell[2]);
+ /* Add influence to emission map */
+ em->influence[index] = 1.0f;
+ /* Uses particle velocity as initial velocity for smoke */
+ if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO))
+ {
+ VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi);
+ }
+ } // particles loop
+ }
+ else if (valid_particles > 0) { // MOD_SMOKE_FLOW_USE_PART_SIZE
+ int min[3], max[3], res[3];
+ const float hr = 1.0f / ((float)hires_multiplier);
+ /* slightly adjust high res antialias smoothness based on number of divisions
+ * to allow smaller details but yet not differing too much from the low res size */
+ const float hr_smooth = smooth * powf(hr, 1.0f / 3.0f);
+
+ /* setup loop bounds */
+ for (int i = 0; i < 3; i++) {
+ min[i] = em->min[i] * hires_multiplier;
+ max[i] = em->max[i] * hires_multiplier;
+ res[i] = em->res[i] * hires_multiplier;
+ }
+
+ BLI_kdtree_balance(tree);
+
+ EmitFromParticlesData data = {
+ .sfs = sfs, .tree = tree, .hires_multiplier = hires_multiplier, .hr = hr,
+ .em = em, .particle_vel = particle_vel, .min = min, .max = max, .res = res,
+ .solid = solid, .smooth = smooth, .hr_smooth = hr_smooth,
+ };
+
+ BLI_task_parallel_range(min[2], max[2], &data, emit_from_particles_task_cb, true);
+ }
+
+ if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE) {
+ BLI_kdtree_free(tree);
+ }
+
+ /* free data */
+ if (particle_pos)
+ MEM_freeN(particle_pos);
+ if (particle_vel)
+ MEM_freeN(particle_vel);
+ }
+}
+
static void sample_derivedmesh(
SmokeFlowSettings *sfs,
const MVert *mvert, const MLoop *mloop, const MLoopTri *mlooptri, const MLoopUV *mloopuv,
@@ -1877,7 +2135,10 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
/* just sample flow directly to emission map if no subframes */
if (!subframes) {
- if (sfs->source == MOD_SMOKE_FLOW_SOURCE_MESH) {
+ if (sfs->source == MOD_SMOKE_FLOW_SOURCE_PARTICLES) {
+ emit_from_particles(collob, sds, sfs, em, scene, dt);
+ }
+ else {
emit_from_derivedmesh(collob, sds, sfs, em, dt);
}
}
@@ -1908,7 +2169,14 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
scene->r.subframe = 0.0f;
}
- if (sfs->source == MOD_SMOKE_FLOW_SOURCE_MESH) {
+ if (sfs->source == MOD_SMOKE_FLOW_SOURCE_PARTICLES) {
+ /* emit_from_particles() updates timestep internally */
+ emit_from_particles(collob, sds, sfs, &em_temp, scene, sdt);
+ if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) {
+ hires_multiplier = 1;
+ }
+ }
+ else { /* MOD_SMOKE_FLOW_SOURCE_MESH */
/* update flow object frame */
BLI_mutex_lock(&object_update_lock);
BKE_object_modifier_update_subframe(scene, collob, true, 5, BKE_scene_frame_get(scene), eModifierType_Smoke);
@@ -2225,7 +2493,7 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds,
ListBase *effectors;
/* make sure smoke flow influence is 0.0f */
sds->effector_weights->weight[PFIELD_SMOKEFLOW] = 0.0f;
- effectors = pdInitEffectors(scene, ob, sds->effector_weights, true);
+ effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights, true);
if (effectors) {
// precalculate wind forces
@@ -2460,16 +2728,28 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
else if (smd->type & MOD_SMOKE_TYPE_DOMAIN)
{
SmokeDomainSettings *sds = smd->domain;
- int startframe = scene->r.sfra, endframe = scene->r.efra, framenr = scene->r.cfra;
+ PointCache *cache = NULL;
+ PTCacheID pid;
+ int startframe, endframe, framenr;
+ float timescale;
+
+ framenr = scene->r.cfra;
//printf("time: %d\n", scene->r.cfra);
+ cache = sds->point_cache[0];
+ BKE_ptcache_id_from_smoke(&pid, ob, smd);
+ BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, &timescale);
+
if (!smd->domain->fluid || framenr == startframe)
{
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
smokeModifier_reset_ex(smd, false);
+ BKE_ptcache_validate(cache, framenr);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
}
- if (!smd->domain->fluid && (framenr != startframe) && (smd->domain->flags & MOD_SMOKE_FILE_LOAD) == 0)
+ if (!smd->domain->fluid && (framenr != startframe) && (smd->domain->flags & MOD_SMOKE_FILE_LOAD) == 0 && (cache->flag & PTCACHE_BAKED) == 0)
return;
smd->domain->flags &= ~MOD_SMOKE_FILE_LOAD;
@@ -2489,6 +2769,13 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
/* don't simulate if viewing start frame, but scene frame is not real start frame */
bool can_simulate = (framenr == (int)smd->time + 1) && (framenr == scene->r.cfra);
+ /* try to read from cache */
+ if (BKE_ptcache_read(&pid, (float)framenr, can_simulate) == PTCACHE_READ_EXACT) {
+ BKE_ptcache_validate(cache, framenr);
+ smd->time = framenr;
+ return;
+ }
+
if (!can_simulate)
return;
@@ -2496,6 +2783,11 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
double start = PIL_check_seconds_timer();
#endif
+ /* if on second frame, write cache for first frame */
+ if ((int)smd->time == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
+ BKE_ptcache_write(&pid, startframe);
+ }
+
// set new time
smd->time = scene->r.cfra;
@@ -2525,6 +2817,10 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
smoke_turbulence_step(sds->wt, sds->fluid);
}
+ BKE_ptcache_validate(cache, framenr);
+ if (framenr != startframe)
+ BKE_ptcache_write(&pid, framenr);
+
#ifdef DEBUG_TIME
double end = PIL_check_seconds_timer();
printf("Frame: %d, Time: %f\n\n", (int)smd->time, (float)(end - start));
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index f0e2cbd657e..660107eb2e6 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -63,7 +63,6 @@ variables on the UI for now
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "DNA_object_force.h"
#include "DNA_group_types.h"
#include "BLI_math.h"
@@ -77,6 +76,7 @@ variables on the UI for now
#include "BKE_global.h"
#include "BKE_modifier.h"
#include "BKE_softbody.h"
+#include "BKE_pointcache.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_scene.h"
@@ -1549,7 +1549,7 @@ static void scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow)
SoftBody *sb = ob->soft;
ListBase *do_effector = NULL;
- do_effector = pdInitEffectors(scene, ob, sb->effector_weights, true);
+ do_effector = pdInitEffectors(scene, ob, NULL, sb->effector_weights, true);
_scan_for_ext_spring_forces(scene, ob, timenow, 0, sb->totspring, do_effector);
pdEndEffectors(&do_effector);
}
@@ -1569,7 +1569,7 @@ static void sb_sfesf_threads_run(Scene *scene, struct Object *ob, float timenow,
int i, totthread, left, dec;
int lowsprings =100; /* wild guess .. may increase with better thread management 'above' or even be UI option sb->spawn_cf_threads_nopts */
- do_effector= pdInitEffectors(scene, ob, ob->soft->effector_weights, true);
+ do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true);
/* figure the number of threads while preventing pretty pointless threading overhead */
totthread= BKE_scene_num_threads(scene);
@@ -2259,7 +2259,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl
sb_sfesf_threads_run(scene, ob, timenow, sb->totspring, NULL);
/* after spring scan because it uses Effoctors too */
- do_effector= pdInitEffectors(scene, ob, sb->effector_weights, true);
+ do_effector= pdInitEffectors(scene, ob, NULL, sb->effector_weights, true);
if (do_deflector) {
float defforce[3];
@@ -2321,7 +2321,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa
if (do_springcollision || do_aero) scan_for_ext_spring_forces(scene, ob, timenow);
/* after spring scan because it uses Effoctors too */
- do_effector= pdInitEffectors(scene, ob, ob->soft->effector_weights, true);
+ do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true);
if (do_deflector) {
float defforce[3];
@@ -3323,6 +3323,8 @@ SoftBody *sbNew(Scene *scene)
sb->shearstiff = 1.0f;
sb->solverflags |= SBSO_OLDERR;
+ sb->pointcache = BKE_ptcache_add(&sb->ptcaches);
+
if (!sb->effector_weights)
sb->effector_weights = BKE_add_effector_weights(NULL);
@@ -3335,6 +3337,8 @@ SoftBody *sbNew(Scene *scene)
void sbFree(SoftBody *sb)
{
free_softbody_intern(sb);
+ BKE_ptcache_free_list(&sb->ptcaches);
+ sb->pointcache = NULL;
if (sb->effector_weights)
MEM_freeN(sb->effector_weights);
MEM_freeN(sb);
@@ -3641,17 +3645,29 @@ static void softbody_step(Scene *scene, Object *ob, SoftBody *sb, float dtime)
void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], int numVerts)
{
SoftBody *sb= ob->soft;
- float dtime, timescale = 1.0f;
- int framedelta = 1, framenr = (int)cfra, startframe = scene->r.sfra, endframe = scene->r.efra;
+ PointCache *cache;
+ PTCacheID pid;
+ float dtime, timescale;
+ int framedelta, framenr, startframe, endframe;
+ int cache_result;
+ cache= sb->pointcache;
+
+ framenr= (int)cfra;
+ framedelta= framenr - cache->simframe;
+
+ BKE_ptcache_id_from_softbody(&pid, ob, sb);
+ BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, &timescale);
/* check for changes in mesh, should only happen in case the mesh
* structure changes during an animation */
if (sb->bpoint && numVerts != sb->totpoint) {
+ BKE_ptcache_invalidate(cache);
return;
}
/* clamp frame ranges */
if (framenr < startframe) {
+ BKE_ptcache_invalidate(cache);
return;
}
else if (framenr > endframe) {
@@ -3688,20 +3704,53 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i
return;
}
if (framenr == startframe) {
+ BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
+
/* first frame, no simulation to do, just set the positions */
softbody_update_positions(ob, sb, vertexCos, numVerts);
+ BKE_ptcache_validate(cache, framenr);
+ cache->flag &= ~PTCACHE_REDO_NEEDED;
+
sb->last_frame = framenr;
return;
}
/* try to read from cache */
- bool can_simulate = (framenr == sb->last_frame + 1);
+ bool can_simulate = (framenr == sb->last_frame + 1) && !(cache->flag & PTCACHE_BAKED);
+
+ cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate);
+
+ if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED ||
+ (!can_simulate && cache_result == PTCACHE_READ_OLD)) {
+ softbody_to_object(ob, vertexCos, numVerts, sb->local);
+
+ BKE_ptcache_validate(cache, framenr);
+
+ if (cache_result == PTCACHE_READ_INTERPOLATED && cache->flag & PTCACHE_REDO_NEEDED)
+ BKE_ptcache_write(&pid, framenr);
+
+ sb->last_frame = framenr;
+
+ return;
+ }
+ else if (cache_result==PTCACHE_READ_OLD) {
+ ; /* do nothing */
+ }
+ else if (/*ob->id.lib || */(cache->flag & PTCACHE_BAKED)) { /* "library linking & pointcaches" has to be solved properly at some point */
+ /* if baked and nothing in cache, do nothing */
+ BKE_ptcache_invalidate(cache);
+ return;
+ }
if (!can_simulate)
return;
+ /* if on second frame, write cache for first frame */
+ if (cache->simframe == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0))
+ BKE_ptcache_write(&pid, startframe);
+
softbody_update_positions(ob, sb, vertexCos, numVerts);
/* checking time: */
@@ -3712,6 +3761,9 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i
softbody_to_object(ob, vertexCos, numVerts, 0);
+ BKE_ptcache_validate(cache, framenr);
+ BKE_ptcache_write(&pid, framenr);
+
sb->last_frame = framenr;
}
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index e98b6b60331..2d3ecad19ad 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -50,6 +50,7 @@
#include "DNA_brush_types.h"
#include "DNA_node_types.h"
#include "DNA_color_types.h"
+#include "DNA_particle_types.h"
#include "DNA_linestyle_types.h"
#include "IMB_imbuf.h"
@@ -1064,6 +1065,10 @@ bool give_active_mtex(ID *id, MTex ***mtex_ar, short *act)
*mtex_ar = ((FreestyleLineStyle *)id)->mtex;
if (act) *act = (((FreestyleLineStyle *)id)->texact);
break;
+ case ID_PA:
+ *mtex_ar = ((ParticleSettings *)id)->mtex;
+ if (act) *act = (((ParticleSettings *)id)->texact);
+ break;
default:
*mtex_ar = NULL;
if (act) *act = 0;
@@ -1091,6 +1096,9 @@ void set_active_mtex(ID *id, short act)
case ID_LS:
((FreestyleLineStyle *)id)->texact = act;
break;
+ case ID_PA:
+ ((ParticleSettings *)id)->texact = act;
+ break;
}
}
@@ -1200,6 +1208,42 @@ void set_current_brush_texture(Brush *br, Tex *newtex)
}
}
+Tex *give_current_particle_texture(ParticleSettings *part)
+{
+ MTex *mtex = NULL;
+ Tex *tex = NULL;
+
+ if (!part) return NULL;
+
+ mtex = part->mtex[(int)(part->texact)];
+ if (mtex) tex = mtex->tex;
+
+ return tex;
+}
+
+void set_current_particle_texture(ParticleSettings *part, Tex *newtex)
+{
+ int act = part->texact;
+
+ if (part->mtex[act] && part->mtex[act]->tex)
+ id_us_min(&part->mtex[act]->tex->id);
+
+ if (newtex) {
+ if (!part->mtex[act]) {
+ part->mtex[act] = BKE_texture_mtex_add();
+ part->mtex[act]->texco = TEXCO_ORCO;
+ part->mtex[act]->blendtype = MTEX_MUL;
+ }
+
+ part->mtex[act]->tex = newtex;
+ id_us_plus(&newtex->id);
+ }
+ else if (part->mtex[act]) {
+ MEM_freeN(part->mtex[act]);
+ part->mtex[act] = NULL;
+ }
+}
+
/* ------------------------------------------------------------------------- */
EnvMap *BKE_texture_envmap_add(void)
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 69fc85b63af..d3576a12b9c 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -81,9 +81,9 @@
#include "DNA_nla_types.h"
#include "DNA_node_types.h"
#include "DNA_object_fluidsim.h" // NT
-#include "DNA_object_force.h"
#include "DNA_object_types.h"
#include "DNA_packedFile_types.h"
+#include "DNA_particle_types.h"
#include "DNA_property_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_text_types.h"
@@ -137,6 +137,8 @@
#include "BKE_node.h" // for tree type defines
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_sca.h" // for init_actuator
#include "BKE_scene.h"
@@ -3983,6 +3985,83 @@ static void direct_link_material(FileData *fd, Material *ma)
}
/* ************ READ PARTICLE SETTINGS ***************** */
+/* update this also to writefile.c */
+static const char *ptcache_data_struct[] = {
+ "", // BPHYS_DATA_INDEX
+ "", // BPHYS_DATA_LOCATION
+ "", // BPHYS_DATA_VELOCITY
+ "", // BPHYS_DATA_ROTATION
+ "", // BPHYS_DATA_AVELOCITY / BPHYS_DATA_XCONST */
+ "", // BPHYS_DATA_SIZE:
+ "", // BPHYS_DATA_TIMES:
+ "BoidData" // case BPHYS_DATA_BOIDS:
+};
+
+static void direct_link_pointcache_cb(FileData *fd, void *data)
+{
+ PTCacheMem *pm = data;
+ PTCacheExtra *extra;
+ int i;
+ for (i = 0; i < BPHYS_TOT_DATA; i++) {
+ pm->data[i] = newdataadr(fd, pm->data[i]);
+
+ /* the cache saves non-struct data without DNA */
+ if (pm->data[i] && ptcache_data_struct[i][0]=='\0' && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) {
+ int tot = (BKE_ptcache_data_size(i) * pm->totpoint) / sizeof(int); /* data_size returns bytes */
+ int *poin = pm->data[i];
+
+ BLI_endian_switch_int32_array(poin, tot);
+ }
+ }
+
+ link_list(fd, &pm->extradata);
+
+ for (extra=pm->extradata.first; extra; extra=extra->next)
+ extra->data = newdataadr(fd, extra->data);
+}
+
+static void direct_link_pointcache(FileData *fd, PointCache *cache)
+{
+ if ((cache->flag & PTCACHE_DISK_CACHE)==0) {
+ link_list_ex(fd, &cache->mem_cache, direct_link_pointcache_cb);
+ }
+ else
+ BLI_listbase_clear(&cache->mem_cache);
+
+ cache->flag &= ~PTCACHE_SIMULATION_VALID;
+ cache->simframe = 0;
+ cache->edit = NULL;
+ cache->free_edit = NULL;
+ cache->cached_frames = NULL;
+}
+
+static void direct_link_pointcache_list(FileData *fd, ListBase *ptcaches, PointCache **ocache, int force_disk)
+{
+ if (ptcaches->first) {
+ PointCache *cache= NULL;
+ link_list(fd, ptcaches);
+ for (cache=ptcaches->first; cache; cache=cache->next) {
+ direct_link_pointcache(fd, cache);
+ if (force_disk) {
+ cache->flag |= PTCACHE_DISK_CACHE;
+ cache->step = 1;
+ }
+ }
+
+ *ocache = newdataadr(fd, *ocache);
+ }
+ else if (*ocache) {
+ /* old "single" caches need to be linked too */
+ *ocache = newdataadr(fd, *ocache);
+ direct_link_pointcache(fd, *ocache);
+ if (force_disk) {
+ (*ocache)->flag |= PTCACHE_DISK_CACHE;
+ (*ocache)->step = 1;
+ }
+
+ ptcaches->first = ptcaches->last = *ocache;
+ }
+}
static void lib_link_partdeflect(FileData *fd, ID *id, PartDeflect *pd)
{
@@ -3992,11 +4071,279 @@ static void lib_link_partdeflect(FileData *fd, ID *id, PartDeflect *pd)
pd->f_source = newlibadr(fd, id->lib, pd->f_source);
}
+static void lib_link_particlesettings(FileData *fd, Main *main)
+{
+ ParticleSettings *part;
+ ParticleDupliWeight *dw;
+ MTex *mtex;
+ int a;
+
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->id.tag & LIB_TAG_NEED_LINK) {
+ lib_link_animdata(fd, &part->id, part->adt);
+ part->ipo = newlibadr_us(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system
+
+ part->dup_ob = newlibadr(fd, part->id.lib, part->dup_ob);
+ part->dup_group = newlibadr(fd, part->id.lib, part->dup_group);
+ part->eff_group = newlibadr(fd, part->id.lib, part->eff_group);
+ part->bb_ob = newlibadr(fd, part->id.lib, part->bb_ob);
+ part->collision_group = newlibadr(fd, part->id.lib, part->collision_group);
+
+ lib_link_partdeflect(fd, &part->id, part->pd);
+ lib_link_partdeflect(fd, &part->id, part->pd2);
+
+ if (part->effector_weights) {
+ part->effector_weights->group = newlibadr(fd, part->id.lib, part->effector_weights->group);
+ }
+ else {
+ part->effector_weights = BKE_add_effector_weights(part->eff_group);
+ }
+
+ if (part->dupliweights.first && part->dup_group) {
+ int index_ok = 0;
+ /* check for old files without indices (all indexes 0) */
+ if (BLI_listbase_is_single(&part->dupliweights)) {
+ /* special case for only one object in the group */
+ index_ok = 1;
+ }
+ else {
+ for (dw = part->dupliweights.first; dw; dw = dw->next) {
+ if (dw->index > 0) {
+ index_ok = 1;
+ break;
+ }
+ }
+ }
+
+ if (index_ok) {
+ /* if we have indexes, let's use them */
+ for (dw = part->dupliweights.first; dw; dw = dw->next) {
+ /* Do not try to restore pointer here, we have to search for group objects in another
+ * separated step.
+ * Reason is, the used group may be linked from another library, which has not yet
+ * been 'lib_linked'.
+ * Since dw->ob is not considered as an object user (it does not make objet directly linked),
+ * we may have no valid way to retrieve it yet.
+ * See T49273. */
+ dw->ob = NULL;
+ }
+ }
+ else {
+ /* otherwise try to get objects from own library (won't work on library linked groups) */
+ for (dw = part->dupliweights.first; dw; dw = dw->next) {
+ dw->ob = newlibadr(fd, part->id.lib, dw->ob);
+ }
+ }
+ }
+ else {
+ BLI_listbase_clear(&part->dupliweights);
+ }
+
+ if (part->boids) {
+ BoidState *state = part->boids->states.first;
+ BoidRule *rule;
+ for (; state; state=state->next) {
+ rule = state->rules.first;
+ for (; rule; rule=rule->next) {
+ switch (rule->type) {
+ case eBoidRuleType_Goal:
+ case eBoidRuleType_Avoid:
+ {
+ BoidRuleGoalAvoid *brga = (BoidRuleGoalAvoid*)rule;
+ brga->ob = newlibadr(fd, part->id.lib, brga->ob);
+ break;
+ }
+ case eBoidRuleType_FollowLeader:
+ {
+ BoidRuleFollowLeader *brfl = (BoidRuleFollowLeader*)rule;
+ brfl->ob = newlibadr(fd, part->id.lib, brfl->ob);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (a = 0; a < MAX_MTEX; a++) {
+ mtex= part->mtex[a];
+ if (mtex) {
+ mtex->tex = newlibadr_us(fd, part->id.lib, mtex->tex);
+ mtex->object = newlibadr(fd, part->id.lib, mtex->object);
+ }
+ }
+
+ part->id.tag &= ~LIB_TAG_NEED_LINK;
+ }
+ }
+}
+
static void direct_link_partdeflect(PartDeflect *pd)
{
if (pd) pd->rng = NULL;
}
+static void direct_link_particlesettings(FileData *fd, ParticleSettings *part)
+{
+ int a;
+
+ part->adt = newdataadr(fd, part->adt);
+ part->pd = newdataadr(fd, part->pd);
+ part->pd2 = newdataadr(fd, part->pd2);
+
+ direct_link_animdata(fd, part->adt);
+ direct_link_partdeflect(part->pd);
+ direct_link_partdeflect(part->pd2);
+
+ part->clumpcurve = newdataadr(fd, part->clumpcurve);
+ if (part->clumpcurve)
+ direct_link_curvemapping(fd, part->clumpcurve);
+ part->roughcurve = newdataadr(fd, part->roughcurve);
+ if (part->roughcurve)
+ direct_link_curvemapping(fd, part->roughcurve);
+
+ part->effector_weights = newdataadr(fd, part->effector_weights);
+ if (!part->effector_weights)
+ part->effector_weights = BKE_add_effector_weights(part->eff_group);
+
+ link_list(fd, &part->dupliweights);
+
+ part->boids = newdataadr(fd, part->boids);
+ part->fluid = newdataadr(fd, part->fluid);
+
+ if (part->boids) {
+ BoidState *state;
+ link_list(fd, &part->boids->states);
+
+ for (state=part->boids->states.first; state; state=state->next) {
+ link_list(fd, &state->rules);
+ link_list(fd, &state->conditions);
+ link_list(fd, &state->actions);
+ }
+ }
+ for (a = 0; a < MAX_MTEX; a++) {
+ part->mtex[a] = newdataadr(fd, part->mtex[a]);
+ }
+}
+
+static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase *particles)
+{
+ ParticleSystem *psys, *psysnext;
+
+ for (psys=particles->first; psys; psys=psysnext) {
+ psysnext = psys->next;
+
+ psys->part = newlibadr_us(fd, id->lib, psys->part);
+ if (psys->part) {
+ ParticleTarget *pt = psys->targets.first;
+
+ for (; pt; pt=pt->next)
+ pt->ob=newlibadr(fd, id->lib, pt->ob);
+
+ psys->parent = newlibadr(fd, id->lib, psys->parent);
+ psys->target_ob = newlibadr(fd, id->lib, psys->target_ob);
+
+ if (psys->clmd) {
+ /* XXX - from reading existing code this seems correct but intended usage of
+ * pointcache /w cloth should be added in 'ParticleSystem' - campbell */
+ psys->clmd->point_cache = psys->pointcache;
+ psys->clmd->ptcaches.first = psys->clmd->ptcaches.last= NULL;
+ psys->clmd->coll_parms->group = newlibadr(fd, id->lib, psys->clmd->coll_parms->group);
+ psys->clmd->modifier.error = NULL;
+ }
+ }
+ else {
+ /* particle modifier must be removed before particle system */
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ BLI_remlink(&ob->modifiers, psmd);
+ modifier_free((ModifierData *)psmd);
+
+ BLI_remlink(particles, psys);
+ MEM_freeN(psys);
+ }
+ }
+}
+static void direct_link_particlesystems(FileData *fd, ListBase *particles)
+{
+ ParticleSystem *psys;
+ ParticleData *pa;
+ int a;
+
+ for (psys=particles->first; psys; psys=psys->next) {
+ psys->particles=newdataadr(fd, psys->particles);
+
+ if (psys->particles && psys->particles->hair) {
+ for (a=0, pa=psys->particles; a<psys->totpart; a++, pa++)
+ pa->hair=newdataadr(fd, pa->hair);
+ }
+
+ if (psys->particles && psys->particles->keys) {
+ for (a=0, pa=psys->particles; a<psys->totpart; a++, pa++) {
+ pa->keys= NULL;
+ pa->totkey= 0;
+ }
+
+ psys->flag &= ~PSYS_KEYED;
+ }
+
+ if (psys->particles && psys->particles->boid) {
+ pa = psys->particles;
+ pa->boid = newdataadr(fd, pa->boid);
+ pa->boid->ground = NULL; /* This is purely runtime data, but still can be an issue if left dangling. */
+ for (a = 1, pa++; a < psys->totpart; a++, pa++) {
+ pa->boid = (pa - 1)->boid + 1;
+ pa->boid->ground = NULL;
+ }
+ }
+ else if (psys->particles) {
+ for (a=0, pa=psys->particles; a<psys->totpart; a++, pa++)
+ pa->boid = NULL;
+ }
+
+ psys->fluid_springs = newdataadr(fd, psys->fluid_springs);
+
+ psys->child = newdataadr(fd, psys->child);
+ psys->effectors = NULL;
+
+ link_list(fd, &psys->targets);
+
+ psys->edit = NULL;
+ psys->free_edit = NULL;
+ psys->pathcache = NULL;
+ psys->childcache = NULL;
+ BLI_listbase_clear(&psys->pathcachebufs);
+ BLI_listbase_clear(&psys->childcachebufs);
+ psys->pdd = NULL;
+ psys->renderdata = NULL;
+
+ if (psys->clmd) {
+ psys->clmd = newdataadr(fd, psys->clmd);
+ psys->clmd->clothObject = NULL;
+ psys->clmd->hairdata = NULL;
+
+ psys->clmd->sim_parms= newdataadr(fd, psys->clmd->sim_parms);
+ psys->clmd->coll_parms= newdataadr(fd, psys->clmd->coll_parms);
+
+ if (psys->clmd->sim_parms) {
+ psys->clmd->sim_parms->effector_weights = NULL;
+ if (psys->clmd->sim_parms->presets > 10)
+ psys->clmd->sim_parms->presets = 0;
+ }
+
+ psys->hair_in_dm = psys->hair_out_dm = NULL;
+ psys->clmd->solver_result = NULL;
+ }
+
+ direct_link_pointcache_list(fd, &psys->ptcaches, &psys->pointcache, 0);
+ if (psys->clmd) {
+ psys->clmd->point_cache = psys->pointcache;
+ }
+
+ psys->tree = NULL;
+ psys->bvhtree = NULL;
+ }
+ return;
+}
+
/* ************ READ MESH ***************** */
static void lib_link_mtface(FileData *fd, Mesh *me, MTFace *mtface, int totface)
@@ -4607,6 +4954,7 @@ static void lib_link_object(FileData *fd, Main *main)
ob->soft->effector_weights->group = newlibadr(fd, ob->id.lib, ob->soft->effector_weights->group);
}
+ lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem);
lib_link_modifiers(fd, ob);
if (ob->rigidbody_constraint) {
@@ -4707,6 +5055,8 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
clmd->sim_parms= newdataadr(fd, clmd->sim_parms);
clmd->coll_parms= newdataadr(fd, clmd->coll_parms);
+ direct_link_pointcache_list(fd, &clmd->ptcaches, &clmd->point_cache, 0);
+
if (clmd->sim_parms) {
if (clmd->sim_parms->presets > 10)
clmd->sim_parms->presets = 0;
@@ -4752,6 +5102,24 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
smd->domain->effector_weights = newdataadr(fd, smd->domain->effector_weights);
if (!smd->domain->effector_weights)
smd->domain->effector_weights = BKE_add_effector_weights(NULL);
+
+ direct_link_pointcache_list(fd, &(smd->domain->ptcaches[0]), &(smd->domain->point_cache[0]), 1);
+
+ /* Smoke uses only one cache from now on, so store pointer convert */
+ if (smd->domain->ptcaches[1].first || smd->domain->point_cache[1]) {
+ if (smd->domain->point_cache[1]) {
+ PointCache *cache = newdataadr(fd, smd->domain->point_cache[1]);
+ if (cache->flag & PTCACHE_FAKE_SMOKE) {
+ /* Smoke was already saved in "new format" and this cache is a fake one. */
+ }
+ else {
+ printf("High resolution smoke cache not available due to pointcache update. Please reset the simulation.\n");
+ }
+ BKE_ptcache_free(cache);
+ }
+ BLI_listbase_clear(&smd->domain->ptcaches[1]);
+ smd->domain->point_cache[1] = NULL;
+ }
}
else if (smd->type == MOD_SMOKE_TYPE_FLOW) {
smd->domain = NULL;
@@ -4761,6 +5129,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
smd->flow->dm = NULL;
smd->flow->verts_old = NULL;
smd->flow->numverts = 0;
+ smd->flow->psys = newdataadr(fd, smd->flow->psys);
}
else if (smd->type == MOD_SMOKE_TYPE_COLL) {
smd->flow = NULL;
@@ -4796,6 +5165,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
for (surface=pmd->canvas->surfaces.first; surface; surface=surface->next) {
surface->canvas = pmd->canvas;
surface->data = NULL;
+ direct_link_pointcache_list(fd, &(surface->ptcaches), &(surface->pointcache), 1);
if (!(surface->effector_weights = newdataadr(fd, surface->effector_weights)))
surface->effector_weights = BKE_add_effector_weights(NULL);
@@ -4805,6 +5175,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
if (pmd->brush) {
pmd->brush = newdataadr(fd, pmd->brush);
pmd->brush->pmd = pmd;
+ pmd->brush->psys = newdataadr(fd, pmd->brush->psys);
pmd->brush->paint_ramp = newdataadr(fd, pmd->brush->paint_ramp);
pmd->brush->vel_ramp = newdataadr(fd, pmd->brush->vel_ramp);
pmd->brush->dm = NULL;
@@ -4859,6 +5230,15 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
direct_link_curvemapping(fd, hmd->curfalloff);
}
}
+ else if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
+
+ psmd->dm_final = NULL;
+ psmd->dm_deformed = NULL;
+ psmd->psys= newdataadr(fd, psmd->psys);
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ psmd->flag |= eParticleSystemFlag_file_loaded;
+ }
else if (md->type == eModifierType_Explode) {
ExplodeModifierData *psmd = (ExplodeModifierData *)md;
@@ -4961,7 +5341,7 @@ static void direct_link_object(FileData *fd, Object *ob)
* See [#34776, #42780] for more information.
*/
if (fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) {
- ob->mode &= ~OB_MODE_EDIT;
+ ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT);
if (!fd->memfile) {
ob->mode &= ~OB_MODE_POSE;
}
@@ -5064,6 +5444,8 @@ static void direct_link_object(FileData *fd, Object *ob)
sb->effector_weights = newdataadr(fd, sb->effector_weights);
if (!sb->effector_weights)
sb->effector_weights = BKE_add_effector_weights(NULL);
+
+ direct_link_pointcache_list(fd, &sb->ptcaches, &sb->pointcache, 0);
}
ob->bsoft = newdataadr(fd, ob->bsoft);
ob->fluidsimSettings= newdataadr(fd, ob->fluidsimSettings); /* NT */
@@ -5081,6 +5463,9 @@ static void direct_link_object(FileData *fd, Object *ob)
ob->rigidbody_constraint = newdataadr(fd, ob->rigidbody_constraint);
if (ob->rigidbody_constraint)
ob->rigidbody_constraint->physics_constraint = NULL;
+
+ link_list(fd, &ob->particlesystem);
+ direct_link_particlesystems(fd, &ob->particlesystem);
link_list(fd, &ob->prop);
for (prop = ob->prop.first; prop; prop = prop->next) {
@@ -5291,6 +5676,8 @@ static void lib_link_scene(FileData *fd, Main *main)
sce->toolsettings->skgen_template = newlibadr(fd, sce->id.lib, sce->toolsettings->skgen_template);
+ sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object);
+
for (base = sce->base.first; base; base = next) {
next = base->next;
@@ -5531,6 +5918,9 @@ static void direct_link_scene(FileData *fd, Scene *sce)
direct_link_paint(fd, &sce->toolsettings->imapaint.paint);
sce->toolsettings->imapaint.paintcursor = NULL;
+ sce->toolsettings->particle.paintcursor = NULL;
+ sce->toolsettings->particle.scene = NULL;
+ sce->toolsettings->particle.object = NULL;
sce->toolsettings->gp_sculpt.paintcursor = NULL;
/* in rare cases this is needed, see [#33806] */
@@ -5724,6 +6114,13 @@ static void direct_link_scene(FileData *fd, Scene *sce)
rbw->effector_weights = newdataadr(fd, rbw->effector_weights);
if (!rbw->effector_weights)
rbw->effector_weights = BKE_add_effector_weights(NULL);
+
+ /* link cache */
+ direct_link_pointcache_list(fd, &rbw->ptcaches, &rbw->pointcache, false);
+ /* make sure simulation starts from the beginning after loading file */
+ if (rbw->pointcache) {
+ rbw->ltime = (float)rbw->pointcache->startframe;
+ }
}
sce->preview = direct_link_preview_image(fd, sce->preview);
@@ -7574,6 +7971,7 @@ static const char *dataname(short id_code)
case ID_SO: return "Data from SO";
case ID_NT: return "Data from NT";
case ID_BR: return "Data from BR";
+ case ID_PA: return "Data from PA";
case ID_PAL: return "Data from PAL";
case ID_PC: return "Data from PCRV";
case ID_GD: return "Data from GD";
@@ -7816,6 +8214,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
case ID_BR:
direct_link_brush(fd, (Brush*)id);
break;
+ case ID_PA:
+ direct_link_particlesettings(fd, (ParticleSettings*)id);
+ break;
case ID_GD:
direct_link_gpencil(fd, (bGPdata *)id);
break;
@@ -8022,6 +8423,7 @@ static void lib_link_all(FileData *fd, Main *main)
lib_link_brush(fd, main);
lib_link_palette(fd, main);
lib_link_paint_curve(fd, main);
+ lib_link_particlesettings(fd, main);
lib_link_movieclip(fd, main);
lib_link_mask(fd, main);
lib_link_linestyle(fd, main);
@@ -8549,6 +8951,58 @@ static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
expand_animdata_nlastrips(fd, mainvar, &nlt->strips);
}
+static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSettings *part)
+{
+ int a;
+
+ expand_doit(fd, mainvar, part->dup_ob);
+ expand_doit(fd, mainvar, part->dup_group);
+ expand_doit(fd, mainvar, part->eff_group);
+ expand_doit(fd, mainvar, part->bb_ob);
+ expand_doit(fd, mainvar, part->collision_group);
+
+ if (part->adt)
+ expand_animdata(fd, mainvar, part->adt);
+
+ for (a = 0; a < MAX_MTEX; a++) {
+ if (part->mtex[a]) {
+ expand_doit(fd, mainvar, part->mtex[a]->tex);
+ expand_doit(fd, mainvar, part->mtex[a]->object);
+ }
+ }
+
+ if (part->effector_weights) {
+ expand_doit(fd, mainvar, part->effector_weights->group);
+ }
+
+ if (part->pd) {
+ expand_doit(fd, mainvar, part->pd->tex);
+ expand_doit(fd, mainvar, part->pd->f_source);
+ }
+ if (part->pd2) {
+ expand_doit(fd, mainvar, part->pd2->tex);
+ expand_doit(fd, mainvar, part->pd2->f_source);
+ }
+
+ if (part->boids) {
+ BoidState *state;
+ BoidRule *rule;
+
+ for (state = part->boids->states.first; state; state = state->next) {
+ for (rule = state->rules.first; rule; rule = rule->next) {
+ if (rule->type == eBoidRuleType_Avoid) {
+ BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
+ expand_doit(fd, mainvar, gabr->ob);
+ }
+ else if (rule->type == eBoidRuleType_FollowLeader) {
+ BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule;
+ expand_doit(fd, mainvar, flbr->ob);
+ }
+ }
+ }
+ }
+}
+
static void expand_group(FileData *fd, Main *mainvar, Group *group)
{
GroupObject *go;
@@ -8851,6 +9305,7 @@ static void expand_object_expandModifiers(
static void expand_object(FileData *fd, Main *mainvar, Object *ob)
{
+ ParticleSystem *psys;
bSensor *sens;
bController *cont;
bActuator *act;
@@ -8907,6 +9362,9 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
if (ob->proxy_group)
expand_doit(fd, mainvar, ob->proxy_group);
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ expand_doit(fd, mainvar, psys->part);
+
for (sens = ob->sensors.first; sens; sens = sens->next) {
if (sens->type == SENS_MESSAGE) {
bMessageSensor *ms = sens->data;
@@ -9277,6 +9735,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
case ID_IP:
expand_ipo(fd, mainvar, (Ipo *)id); // XXX deprecated - old animation system
break;
+ case ID_PA:
+ expand_particlesettings(fd, mainvar, (ParticleSettings *)id);
+ break;
case ID_MC:
expand_movieclip(fd, mainvar, (MovieClip *)id);
break;
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 631aec545c2..1956a17d57b 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -53,7 +53,6 @@
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_fluidsim.h" // NT
-#include "DNA_object_force.h"
#include "DNA_object_types.h"
#include "DNA_view3d_types.h"
#include "DNA_screen_types.h"
@@ -79,6 +78,8 @@
#include "BKE_mesh.h" // for ME_ defines (patching)
#include "BKE_modifier.h"
#include "BKE_multires.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_screen.h"
#include "BKE_sequencer.h"
#include "BKE_texture.h"
@@ -742,6 +743,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
Curve *cu;
Scene *sce;
Tex *tx;
+ ParticleSettings *part;
Object *ob;
//PTCacheID *pid;
//ListBase pidlist;
@@ -872,6 +874,25 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
me->drawflag = ME_DRAWEDGES|ME_DRAWFACES|ME_DRAWCREASES;
}
+ /* particle draw and render types */
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->draw_as) {
+ if (part->draw_as == PART_DRAW_DOT) {
+ part->ren_as = PART_DRAW_HALO;
+ part->draw_as = PART_DRAW_REND;
+ }
+ else if (part->draw_as <= PART_DRAW_AXIS) {
+ part->ren_as = PART_DRAW_HALO;
+ }
+ else {
+ part->ren_as = part->draw_as;
+ part->draw_as = PART_DRAW_REND;
+ }
+ }
+ part->path_end = 1.0f;
+ part->clength = 1.0f;
+ }
+
/* set old pointcaches to have disk cache flag */
for (ob = main->object.first; ob; ob = ob->id.next) {
@@ -1115,6 +1136,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
Lamp *la;
World *wo;
Tex *tex;
+ ParticleSettings *part;
bool do_gravity = false;
for (sce = main->scene.first; sce; sce = sce->id.next)
@@ -1175,6 +1197,12 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
}
}
+ /* Assign proper global gravity weights for dynamics (only z-coordinate is taken into account) */
+ if (do_gravity) {
+ for (part = main->particle.first; part; part = part->id.next)
+ part->effector_weights->global_gravity = part->acc[2]/-9.81f;
+ }
+
for (ob = main->object.first; ob; ob = ob->id.next) {
ModifierData *md;
@@ -1416,9 +1444,14 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
}
if (main->versionfile < 250 || (main->versionfile == 250 && main->subversionfile < 9)) {
+ Scene *sce;
Mesh *me;
Object *ob;
+ for (sce = main->scene.first; sce; sce = sce->id.next)
+ if (!sce->toolsettings->particle.selectmode)
+ sce->toolsettings->particle.selectmode = SCE_SELECT_PATH;
+
if (main->versionfile == 250 && main->subversionfile > 1) {
for (me = main->mesh.first; me; me = me->id.next)
multires_load_old_250(me);
@@ -1747,6 +1780,15 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
SEQ_END
}
+ /* particle brush strength factor was changed from int to float */
+ for (sce = main->scene.first; sce; sce = sce->id.next) {
+ ParticleEditSettings *pset = &sce->toolsettings->particle;
+ int a;
+
+ for (a = 0; a < PE_TOT_BRUSH; a++)
+ pset->brush[a].strength /= 100.0f;
+ }
+
for (ma = main->mat.first; ma; ma = ma->id.next)
if (ma->mode & MA_TRACEBLE)
ma->shade_flag |= MA_APPROX_OCCLUSION;
@@ -2153,6 +2195,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
if (main->versionfile < 255 || (main->versionfile == 255 && main->subversionfile < 1)) {
Brush *br;
+ ParticleSettings *part;
bScreen *sc;
Object *ob;
@@ -2161,6 +2204,14 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
br->ob_mode = OB_MODE_ALL_PAINT;
}
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->boids)
+ part->boids->pitch = 1.0f;
+
+ part->flag &= ~PART_HAIR_REGROW; /* this was a deprecated flag before */
+ part->kink_amp_clump = 1.f; /* keep old files looking similar */
+ }
+
for (sc = main->screen.first; sc; sc = sc->id.next) {
ScrArea *sa;
for (sa = sc->areabase.first; sa; sa = sa->next) {
@@ -2378,6 +2429,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
bScreen *sc;
Brush *brush;
Object *ob;
+ ParticleSettings *part;
Material *mat;
int tex_nr, transp_tex;
@@ -2427,6 +2479,12 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
}
}
}
+
+ /* particle draw color from material */
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->draw & PART_DRAW_MAT_COL)
+ part->draw_col = PART_DRAW_COL_MAT;
+ }
}
if (main->versionfile < 256 || (main->versionfile == 256 && main->subversionfile < 6)) {
@@ -2519,6 +2577,14 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
}
}
}
+
+ {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ /* Initialize particle billboard scale */
+ part->bb_size[0] = part->bb_size[1] = 1.0f;
+ }
+ }
}
if (main->versionfile < 259 || (main->versionfile == 259 && main->subversionfile < 1)) {
@@ -2687,6 +2753,15 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *main)
if (main->versionfile < 259 || (main->versionfile == 259 && main->subversionfile < 4)) {
{
+ /* Adaptive time step for particle systems */
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ part->courant_target = 0.2f;
+ part->time_flag &= ~PART_TIME_AUTOSF;
+ }
+ }
+
+ {
/* set defaults for obstacle avoidance, recast data */
Scene *sce;
for (sce = main->scene.first; sce; sce = sce->id.next) {
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 3e4b6534ad8..907baab0aee 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -36,14 +36,12 @@
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_constraint_types.h"
-#include "DNA_dynamicpaint_types.h"
#include "DNA_genfile.h"
#include "DNA_key_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_fluidsim.h" // NT
-#include "DNA_object_force.h"
#include "DNA_object_types.h"
#include "DNA_property_types.h"
#include "DNA_text_types.h"
@@ -66,6 +64,8 @@
#include "BKE_main.h" // for Main
#include "BKE_mesh.h" // for ME_ defines (patching)
#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_property.h" // for BKE_bproperty_object_get
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -651,6 +651,20 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
for (ntree = main->nodetree.first; ntree; ntree = ntree->id.next)
do_versions_nodetree_image_default_alpha_output(ntree);
}
+
+ {
+ /* support old particle dupliobject rotation settings */
+ ParticleSettings *part;
+
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
+ part->draw |= PART_DRAW_ROTATE_OB;
+
+ if (part->rotmode == 0)
+ part->rotmode = PART_ROT_VEL;
+ }
+ }
+ }
}
if (main->versionfile < 260 || (main->versionfile == 260 && main->subversionfile < 1)) {
@@ -1127,6 +1141,16 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
+
+
+ if (main->versionfile < 263) {
+ /* Default for old files is to save particle rotations to pointcache */
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ part->flag |= PART_ROTATIONS;
+ }
+ }
+
if (main->versionfile < 263 || (main->versionfile == 263 && main->subversionfile < 1)) {
/* file output node paths are now stored in the file info struct instead socket name */
Scene *sce;
@@ -1420,6 +1444,8 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
}
if (main->versionfile < 263 || (main->versionfile == 263 && main->subversionfile < 14)) {
+ ParticleSettings *part;
+
FOREACH_NODETREE(main, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
bNode *node;
@@ -1434,6 +1460,12 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
} FOREACH_NODETREE_END
+
+ /* keep compatibility for dupliobject particle size */
+ for (part = main->particle.first; part; part = part->id.next)
+ if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR))
+ if ((part->draw & PART_DRAW_ROTATE_OB) == 0)
+ part->draw |= PART_DRAW_NO_SCALE_OB;
}
if (main->versionfile < 263 || (main->versionfile == 263 && main->subversionfile < 17)) {
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index c9335f65924..ea654ad6906 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -49,6 +49,7 @@
#include "DNA_mask_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
+#include "DNA_particle_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_actuator_types.h"
#include "DNA_view3d_types.h"
@@ -446,6 +447,22 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
+ if (!MAIN_VERSION_ATLEAST(main, 271, 6)) {
+ Object *ob;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ ModifierData *md;
+
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
+ if (pmd->psys && pmd->psys->clmd) {
+ pmd->psys->clmd->sim_parms->vel_damping = 1.0f;
+ }
+ }
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(main, 272, 0)) {
if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) {
Scene *scene;
@@ -526,6 +543,16 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
+ if (!MAIN_VERSION_ATLEAST(main, 273, 3)) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->clumpcurve)
+ part->child_flag |= PART_CHILD_USE_CLUMP_CURVE;
+ if (part->roughcurve)
+ part->child_flag |= PART_CHILD_USE_ROUGH_CURVE;
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(main, 273, 6)) {
if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) {
Object *ob;
@@ -536,6 +563,39 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
ClothModifierData *clmd = (ClothModifierData *)md;
clmd->sim_parms->bending_damping = 0.5f;
}
+ else if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
+ if (pmd->psys->clmd) {
+ pmd->psys->clmd->sim_parms->bending_damping = 0.5f;
+ }
+ }
+ }
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ part->clump_noise_size = 1.0f;
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ part->kink_extra_steps = 4;
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "MTex", "float", "kinkampfac")) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ int a;
+ for (a = 0; a < MAX_MTEX; a++) {
+ MTex *mtex = part->mtex[a];
+ if (mtex) {
+ mtex->kinkampfac = 1.0f;
+ }
}
}
}
@@ -622,6 +682,19 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
if (!MAIN_VERSION_ATLEAST(main, 274, 1)) {
+ /* particle systems need to be forced to redistribute for jitter mode fix */
+ {
+ Object *ob;
+ ParticleSystem *psys;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if ((psys->pointcache->flag & PTCACHE_BAKED) == 0) {
+ psys->recalc |= PSYS_RECALC_RESET;
+ }
+ }
+ }
+ }
+
/* hysteresis setted to 10% but not actived */
if (!DNA_struct_elem_find(fd->filesdna, "LodLevel", "int", "obhysteresis")) {
Object *ob;
@@ -1016,6 +1089,15 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
if (!MAIN_VERSION_ATLEAST(main, 277, 1)) {
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ ParticleEditSettings *pset = &scene->toolsettings->particle;
+ for (int a = 0; a < PE_TOT_BRUSH; a++) {
+ if (pset->brush[a].strength > 1.0f) {
+ pset->brush[a].strength *= 0.01f;
+ }
+ }
+ }
+
for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
@@ -1173,6 +1255,12 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
ClothModifierData *clmd = (ClothModifierData *)md;
clmd->sim_parms->time_scale = 1.0f;
}
+ else if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
+ if (pmd->psys->clmd) {
+ pmd->psys->clmd->sim_parms->time_scale = 1.0f;
+ }
+ }
}
}
}
@@ -1342,6 +1430,18 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
if (!MAIN_VERSION_ATLEAST(main, 278, 3)) {
+ for (Scene *scene = main->scene.first; scene != NULL; scene = scene->id.next) {
+ if (scene->toolsettings != NULL) {
+ ToolSettings *ts = scene->toolsettings;
+ ParticleEditSettings *pset = &ts->particle;
+ for (int a = 0; a < PE_TOT_BRUSH; a++) {
+ if (pset->brush[a].count == 0) {
+ pset->brush[a].count = 10;
+ }
+ }
+ }
+ }
+
if (!DNA_struct_elem_find(fd->filesdna, "RigidBodyCon", "float", "spring_stiffness_ang_x")) {
Object *ob;
for (ob = main->object.first; ob; ob = ob->id.next) {
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 01ef5d6a606..99d9e140481 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -143,6 +143,13 @@ void BLO_update_defaults_startup_blend(Main *bmain)
ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE;
ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE;
ts->gpencil_ima_align = GP_PROJECT_VIEWSPACE;
+
+ ParticleEditSettings *pset = &ts->particle;
+ for (int a = 0; a < PE_TOT_BRUSH; a++) {
+ pset->brush[a].strength = 0.5f;
+ pset->brush[a].count = 10;
+ }
+ pset->brush[PE_BRUSH_CUT].strength = 1.0f;
}
scene->gm.lodflag |= SCE_LOD_USE_HYST;
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index f5e9fb240dc..f2d42849bcc 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -59,7 +59,6 @@
#include "DNA_nla_types.h"
#include "DNA_node_types.h"
#include "DNA_object_fluidsim.h" // NT
-#include "DNA_object_force.h"
#include "DNA_object_types.h"
#include "DNA_property_types.h"
#include "DNA_view3d_types.h"
@@ -89,6 +88,8 @@
#include "BKE_main.h" // for Main
#include "BKE_mesh.h" // for ME_ defines (patching)
#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_property.h" // for BKE_bproperty_object_get
#include "BKE_scene.h"
#include "BKE_sequencer.h"
@@ -494,6 +495,27 @@ static void do_version_ntree_242_2(bNodeTree *ntree)
}
}
+static void do_version_free_effect_245(Effect *eff)
+{
+ PartEff *paf;
+
+ if (eff->type == EFF_PARTICLE) {
+ paf = (PartEff *)eff;
+ if (paf->keys)
+ MEM_freeN(paf->keys);
+ }
+ MEM_freeN(eff);
+}
+
+static void do_version_free_effects_245(ListBase *lb)
+{
+ Effect *eff;
+
+ while ((eff = BLI_pophead(lb))) {
+ do_version_free_effect_245(eff);
+ }
+}
+
static void do_version_constraints_245(ListBase *lb)
{
bConstraint *con;
@@ -2665,10 +2687,13 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main)
Image *ima;
Lamp *la;
Material *ma;
+ ParticleSettings *part;
World *wrld;
Mesh *me;
bNodeTree *ntree;
Tex *tex;
+ ModifierData *md;
+ ParticleSystem *psys;
/* unless the file was created 2.44.3 but not 2.45, update the constraints */
if (!(main->versionfile == 244 && main->subversionfile == 3) &&
@@ -2749,6 +2774,33 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main)
}
}
+ /* add point caches */
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ if (ob->soft && !ob->soft->pointcache)
+ ob->soft->pointcache = BKE_ptcache_add(&ob->soft->ptcaches);
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (psys->pointcache) {
+ if (psys->pointcache->flag & PTCACHE_BAKED && (psys->pointcache->flag & PTCACHE_DISK_CACHE) == 0) {
+ printf("Old memory cache isn't supported for particles, so re-bake the simulation!\n");
+ psys->pointcache->flag &= ~PTCACHE_BAKED;
+ }
+ }
+ else
+ psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
+ }
+
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Cloth) {
+ ClothModifierData *clmd = (ClothModifierData*) md;
+ if (!clmd->point_cache) {
+ clmd->point_cache = BKE_ptcache_add(&clmd->ptcaches);
+ clmd->point_cache->step = 1;
+ }
+ }
+ }
+ }
+
/* Copy over old per-level multires vertex data
* into a single vertex array in struct Multires */
for (me = main->mesh.first; me; me = me->id.next) {
@@ -2794,6 +2846,18 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main)
ma->strand_min = 1.0f;
}
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->ren_child_nbr == 0)
+ part->ren_child_nbr = part->child_nbr;
+
+ if (part->simplify_refsize == 0) {
+ part->simplify_refsize = 1920;
+ part->simplify_rate = 1.0f;
+ part->simplify_transition = 0.1f;
+ part->simplify_viewport = 0.8f;
+ }
+ }
+
for (wrld = main->world.first; wrld; wrld = wrld->id.next) {
if (wrld->ao_approx_error == 0.0f)
wrld->ao_approx_error = 0.25f;
@@ -2933,7 +2997,9 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main)
}
if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 8)) {
+ Scene *sce;
Object *ob;
+ PartEff *paf = NULL;
for (ob = main->object.first; ob; ob = ob->id.next) {
if (ob->soft && ob->soft->keys) {
@@ -2950,6 +3016,145 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main)
sb->keys = NULL;
sb->totkey = 0;
}
+
+ /* convert old particles to new system */
+ if ((paf = blo_do_version_give_parteff_245(ob))) {
+ ParticleSystem *psys;
+ ModifierData *md;
+ ParticleSystemModifierData *psmd;
+ ParticleSettings *part;
+
+ /* create new particle system */
+ psys = MEM_callocN(sizeof(ParticleSystem), "particle_system");
+ psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
+
+ part = psys->part = psys_new_settings("ParticleSettings", main);
+
+ /* needed for proper libdata lookup */
+ blo_do_versions_oldnewmap_insert(fd->libmap, psys->part, psys->part, 0);
+ part->id.lib = ob->id.lib;
+
+ part->id.us--;
+ part->id.tag |= (ob->id.tag & LIB_TAG_NEED_LINK);
+
+ psys->totpart = 0;
+ psys->flag = PSYS_CURRENT;
+
+ BLI_addtail(&ob->particlesystem, psys);
+
+ md = modifier_new(eModifierType_ParticleSystem);
+ BLI_snprintf(md->name, sizeof(md->name), "ParticleSystem %i", BLI_listbase_count(&ob->particlesystem));
+ psmd = (ParticleSystemModifierData*) md;
+ psmd->psys = psys;
+ BLI_addtail(&ob->modifiers, md);
+
+ /* convert settings from old particle system */
+ /* general settings */
+ part->totpart = MIN2(paf->totpart, 100000);
+ part->sta = paf->sta;
+ part->end = paf->end;
+ part->lifetime = paf->lifetime;
+ part->randlife = paf->randlife;
+ psys->seed = paf->seed;
+ part->disp = paf->disp;
+ part->omat = paf->mat[0];
+ part->hair_step = paf->totkey;
+
+ part->eff_group = paf->group;
+
+ /* old system didn't interpolate between keypoints at render time */
+ part->draw_step = part->ren_step = 0;
+
+ /* physics */
+ part->normfac = paf->normfac * 25.0f;
+ part->obfac = paf->obfac;
+ part->randfac = paf->randfac * 25.0f;
+ part->dampfac = paf->damp;
+ copy_v3_v3(part->acc, paf->force);
+
+ /* flags */
+ if (paf->stype & PAF_VECT) {
+ if (paf->flag & PAF_STATIC) {
+ /* new hair lifetime is always 100.0f */
+ float fac = paf->lifetime / 100.0f;
+
+ part->draw_as = PART_DRAW_PATH;
+ part->type = PART_HAIR;
+ psys->recalc |= PSYS_RECALC_REDO;
+
+ part->normfac *= fac;
+ part->randfac *= fac;
+ }
+ else {
+ part->draw_as = PART_DRAW_LINE;
+ part->draw |= PART_DRAW_VEL_LENGTH;
+ part->draw_line[1] = 0.04f;
+ }
+ }
+
+ part->rotmode = PART_ROT_VEL;
+
+ part->flag |= (paf->flag & PAF_BSPLINE) ? PART_HAIR_BSPLINE : 0;
+ part->flag |= (paf->flag & PAF_TRAND) ? PART_TRAND : 0;
+ part->flag |= (paf->flag & PAF_EDISTR) ? PART_EDISTR : 0;
+ part->flag |= (paf->flag & PAF_UNBORN) ? PART_UNBORN : 0;
+ part->flag |= (paf->flag & PAF_DIED) ? PART_DIED : 0;
+ part->from |= (paf->flag & PAF_FACE) ? PART_FROM_FACE : 0;
+ part->draw |= (paf->flag & PAF_SHOWE) ? PART_DRAW_EMITTER : 0;
+
+ psys->vgroup[PSYS_VG_DENSITY] = paf->vertgroup;
+ psys->vgroup[PSYS_VG_VEL] = paf->vertgroup_v;
+ psys->vgroup[PSYS_VG_LENGTH] = paf->vertgroup_v;
+
+ /* dupliobjects */
+ if (ob->transflag & OB_DUPLIVERTS) {
+ Object *dup = main->object.first;
+
+ for (; dup; dup = dup->id.next) {
+ if (ob == blo_do_versions_newlibadr(fd, lib, dup->parent)) {
+ part->dup_ob = dup;
+ ob->transflag |= OB_DUPLIPARTS;
+ ob->transflag &= ~OB_DUPLIVERTS;
+
+ part->draw_as = PART_DRAW_OB;
+
+ /* needed for proper libdata lookup */
+ blo_do_versions_oldnewmap_insert(fd->libmap, dup, dup, 0);
+ }
+ }
+ }
+
+ {
+ FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
+ if (fluidmd && fluidmd->fss && fluidmd->fss->type == OB_FLUIDSIM_PARTICLE)
+ part->type = PART_FLUID;
+ }
+
+ do_version_free_effects_245(&ob->effect);
+
+ printf("Old particle system converted to new system.\n");
+ }
+ }
+
+ for (sce = main->scene.first; sce; sce = sce->id.next) {
+ ParticleEditSettings *pset = &sce->toolsettings->particle;
+ int a;
+
+ if (pset->brush[0].size == 0) {
+ pset->flag = PE_KEEP_LENGTHS|PE_LOCK_FIRST|PE_DEFLECT_EMITTER;
+ pset->emitterdist = 0.25f;
+ pset->totrekey = 5;
+ pset->totaddkey = 5;
+ pset->brushtype = PE_BRUSH_NONE;
+
+ for (a = 0; a < PE_TOT_BRUSH; a++) {
+ pset->brush[a].strength = 50;
+ pset->brush[a].size = 50;
+ pset->brush[a].step = 10;
+ }
+
+ pset->brush[PE_BRUSH_CUT].strength = 100;
+ }
}
}
if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 9)) {
@@ -3041,6 +3246,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main)
idproperties_fix_group_lengths(main->action);
idproperties_fix_group_lengths(main->nodetree);
idproperties_fix_group_lengths(main->brush);
+ idproperties_fix_group_lengths(main->particle);
}
/* sun/sky */
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 169ca10f652..27db83055bb 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -129,6 +129,7 @@
#include "DNA_object_types.h"
#include "DNA_object_force.h"
#include "DNA_packedFile_types.h"
+#include "DNA_particle_types.h"
#include "DNA_property_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
@@ -169,6 +170,7 @@
#include "BKE_subsurf.h"
#include "BKE_modifier.h"
#include "BKE_fcurve.h"
+#include "BKE_pointcache.h"
#include "BKE_mesh.h"
#ifdef USE_NODE_COMPAT_CUSTOMNODES
@@ -1186,6 +1188,210 @@ static void write_userdef(WriteData *wd)
}
}
+static void write_boid_state(WriteData *wd, BoidState *state)
+{
+ BoidRule *rule = state->rules.first;
+
+ writestruct(wd, DATA, BoidState, 1, state);
+
+ for (; rule; rule = rule->next) {
+ switch (rule->type) {
+ case eBoidRuleType_Goal:
+ case eBoidRuleType_Avoid:
+ writestruct(wd, DATA, BoidRuleGoalAvoid, 1, rule);
+ break;
+ case eBoidRuleType_AvoidCollision:
+ writestruct(wd, DATA, BoidRuleAvoidCollision, 1, rule);
+ break;
+ case eBoidRuleType_FollowLeader:
+ writestruct(wd, DATA, BoidRuleFollowLeader, 1, rule);
+ break;
+ case eBoidRuleType_AverageSpeed:
+ writestruct(wd, DATA, BoidRuleAverageSpeed, 1, rule);
+ break;
+ case eBoidRuleType_Fight:
+ writestruct(wd, DATA, BoidRuleFight, 1, rule);
+ break;
+ default:
+ writestruct(wd, DATA, BoidRule, 1, rule);
+ break;
+ }
+ }
+#if 0
+ BoidCondition *cond = state->conditions.first;
+ for (; cond; cond = cond->next) {
+ writestruct(wd, DATA, BoidCondition, 1, cond);
+ }
+#endif
+}
+
+/* update this also to readfile.c */
+static const char *ptcache_data_struct[] = {
+ "", // BPHYS_DATA_INDEX
+ "", // BPHYS_DATA_LOCATION
+ "", // BPHYS_DATA_VELOCITY
+ "", // BPHYS_DATA_ROTATION
+ "", // BPHYS_DATA_AVELOCITY / BPHYS_DATA_XCONST */
+ "", // BPHYS_DATA_SIZE:
+ "", // BPHYS_DATA_TIMES:
+ "BoidData" // case BPHYS_DATA_BOIDS:
+};
+static const char *ptcache_extra_struct[] = {
+ "",
+ "ParticleSpring"
+};
+static void write_pointcaches(WriteData *wd, ListBase *ptcaches)
+{
+ PointCache *cache = ptcaches->first;
+ int i;
+
+ for (; cache; cache = cache->next) {
+ writestruct(wd, DATA, PointCache, 1, cache);
+
+ if ((cache->flag & PTCACHE_DISK_CACHE) == 0) {
+ PTCacheMem *pm = cache->mem_cache.first;
+
+ for (; pm; pm = pm->next) {
+ PTCacheExtra *extra = pm->extradata.first;
+
+ writestruct(wd, DATA, PTCacheMem, 1, pm);
+
+ for (i = 0; i < BPHYS_TOT_DATA; i++) {
+ if (pm->data[i] && pm->data_types & (1 << i)) {
+ if (ptcache_data_struct[i][0] == '\0') {
+ writedata(wd, DATA, MEM_allocN_len(pm->data[i]), pm->data[i]);
+ }
+ else {
+ writestruct_id(wd, DATA, ptcache_data_struct[i], pm->totpoint, pm->data[i]);
+ }
+ }
+ }
+
+ for (; extra; extra = extra->next) {
+ if (ptcache_extra_struct[extra->type][0] == '\0') {
+ continue;
+ }
+ writestruct(wd, DATA, PTCacheExtra, 1, extra);
+ writestruct_id(wd, DATA, ptcache_extra_struct[extra->type], extra->totdata, extra->data);
+ }
+ }
+ }
+ }
+}
+static void write_particlesettings(WriteData *wd, ListBase *idbase)
+{
+ ParticleSettings *part;
+ ParticleDupliWeight *dw;
+ GroupObject *go;
+ int a;
+
+ part = idbase->first;
+ while (part) {
+ if (part->id.us > 0 || wd->current) {
+ /* write LibData */
+ writestruct(wd, ID_PA, ParticleSettings, 1, part);
+ write_iddata(wd, &part->id);
+
+ if (part->adt) {
+ write_animdata(wd, part->adt);
+ }
+ writestruct(wd, DATA, PartDeflect, 1, part->pd);
+ writestruct(wd, DATA, PartDeflect, 1, part->pd2);
+ writestruct(wd, DATA, EffectorWeights, 1, part->effector_weights);
+
+ if (part->clumpcurve) {
+ write_curvemapping(wd, part->clumpcurve);
+ }
+ if (part->roughcurve) {
+ write_curvemapping(wd, part->roughcurve);
+ }
+
+ dw = part->dupliweights.first;
+ for (; dw; dw = dw->next) {
+ /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */
+ if (dw->ob != NULL) {
+ dw->index = 0;
+ if (part->dup_group) { /* can be NULL if lining fails or set to None */
+ for (go = part->dup_group->gobject.first; go && go->ob != dw->ob; go = go->next, dw->index++);
+ }
+ }
+ writestruct(wd, DATA, ParticleDupliWeight, 1, dw);
+ }
+
+ if (part->boids && part->phystype == PART_PHYS_BOIDS) {
+ BoidState *state = part->boids->states.first;
+
+ writestruct(wd, DATA, BoidSettings, 1, part->boids);
+
+ for (; state; state = state->next) {
+ write_boid_state(wd, state);
+ }
+ }
+ if (part->fluid && part->phystype == PART_PHYS_FLUID) {
+ writestruct(wd, DATA, SPHFluidSettings, 1, part->fluid);
+ }
+
+ for (a = 0; a < MAX_MTEX; a++) {
+ if (part->mtex[a]) {
+ writestruct(wd, DATA, MTex, 1, part->mtex[a]);
+ }
+ }
+ }
+ part = part->id.next;
+ }
+}
+static void write_particlesystems(WriteData *wd, ListBase *particles)
+{
+ ParticleSystem *psys = particles->first;
+ ParticleTarget *pt;
+ int a;
+
+ for (; psys; psys = psys->next) {
+ writestruct(wd, DATA, ParticleSystem, 1, psys);
+
+ if (psys->particles) {
+ writestruct(wd, DATA, ParticleData, psys->totpart, psys->particles);
+
+ if (psys->particles->hair) {
+ ParticleData *pa = psys->particles;
+
+ for (a = 0; a < psys->totpart; a++, pa++) {
+ writestruct(wd, DATA, HairKey, pa->totkey, pa->hair);
+ }
+ }
+
+ if (psys->particles->boid &&
+ (psys->part->phystype == PART_PHYS_BOIDS))
+ {
+ writestruct(wd, DATA, BoidParticle, psys->totpart, psys->particles->boid);
+ }
+
+ if (psys->part->fluid &&
+ (psys->part->phystype == PART_PHYS_FLUID) &&
+ (psys->part->fluid->flag & SPH_VISCOELASTIC_SPRINGS))
+ {
+ writestruct(wd, DATA, ParticleSpring, psys->tot_fluidsprings, psys->fluid_springs);
+ }
+ }
+ pt = psys->targets.first;
+ for (; pt; pt = pt->next) {
+ writestruct(wd, DATA, ParticleTarget, 1, pt);
+ }
+
+ if (psys->child) {
+ writestruct(wd, DATA, ChildParticle, psys->totchild, psys->child);
+ }
+
+ if (psys->clmd) {
+ writestruct(wd, DATA, ClothModifierData, 1, psys->clmd);
+ writestruct(wd, DATA, ClothSimSettings, 1, psys->clmd->sim_parms);
+ writestruct(wd, DATA, ClothCollSettings, 1, psys->clmd->coll_parms);
+ }
+
+ write_pointcaches(wd, &psys->ptcaches);
+ }
+}
+
static void write_properties(WriteData *wd, ListBase *lb)
{
bProperty *prop;
@@ -1509,6 +1715,7 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
writestruct(wd, DATA, ClothSimSettings, 1, clmd->sim_parms);
writestruct(wd, DATA, ClothCollSettings, 1, clmd->coll_parms);
writestruct(wd, DATA, EffectorWeights, 1, clmd->sim_parms->effector_weights);
+ write_pointcaches(wd, &clmd->ptcaches);
}
else if (md->type == eModifierType_Smoke) {
SmokeModifierData *smd = (SmokeModifierData *)md;
@@ -1517,11 +1724,24 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
writestruct(wd, DATA, SmokeDomainSettings, 1, smd->domain);
if (smd->domain) {
+ write_pointcaches(wd, &(smd->domain->ptcaches[0]));
+
+ /* create fake pointcache so that old blender versions can read it */
+ smd->domain->point_cache[1] = BKE_ptcache_add(&smd->domain->ptcaches[1]);
+ smd->domain->point_cache[1]->flag |= PTCACHE_DISK_CACHE | PTCACHE_FAKE_SMOKE;
+ smd->domain->point_cache[1]->step = 1;
+
+ write_pointcaches(wd, &(smd->domain->ptcaches[1]));
if (smd->domain->coba) {
writestruct(wd, DATA, ColorBand, 1, smd->domain->coba);
}
+
+ /* cleanup the fake pointcache */
+ BKE_ptcache_free_list(&smd->domain->ptcaches[1]);
+ smd->domain->point_cache[1] = NULL;
+
writestruct(wd, DATA, EffectorWeights, 1, smd->domain->effector_weights);
}
}
@@ -1550,6 +1770,8 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
}
/* write caches and effector weights */
for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
+ write_pointcaches(wd, &(surface->ptcaches));
+
writestruct(wd, DATA, EffectorWeights, 1, surface->effector_weights);
}
}
@@ -1649,6 +1871,7 @@ static void write_objects(WriteData *wd, ListBase *idbase)
writestruct(wd, DATA, PartDeflect, 1, ob->pd);
writestruct(wd, DATA, SoftBody, 1, ob->soft);
if (ob->soft) {
+ write_pointcaches(wd, &ob->soft->ptcaches);
writestruct(wd, DATA, EffectorWeights, 1, ob->soft->effector_weights);
}
writestruct(wd, DATA, BulletSoftBody, 1, ob->bsoft);
@@ -1665,6 +1888,7 @@ static void write_objects(WriteData *wd, ListBase *idbase)
writestruct(wd, DATA, ImageUser, 1, ob->iuser);
}
+ write_particlesystems(wd, &ob->particlesystem);
write_modifiers(wd, &ob->modifiers);
writelist(wd, DATA, LinkData, &ob->pc_ids);
@@ -2611,6 +2835,7 @@ static void write_scenes(WriteData *wd, ListBase *scebase)
if (sce->rigidbody_world) {
writestruct(wd, DATA, RigidBodyWorld, 1, sce->rigidbody_world);
writestruct(wd, DATA, EffectorWeights, 1, sce->rigidbody_world->effector_weights);
+ write_pointcaches(wd, &(sce->rigidbody_world->ptcaches));
}
write_previews(wd, sce->preview);
@@ -3868,6 +4093,7 @@ static bool write_file_handle(
write_materials(wd, &mainvar->mat);
write_textures(wd, &mainvar->tex);
write_meshes(wd, &mainvar->mesh);
+ write_particlesettings(wd, &mainvar->particle);
write_nodetrees(wd, &mainvar->nodetree);
write_brushes(wd, &mainvar->brush);
write_palettes(wd, &mainvar->palettes);
diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h
index 5aa49cddfb0..1d76077c9f1 100644
--- a/source/blender/blentranslation/BLT_translation.h
+++ b/source/blender/blentranslation/BLT_translation.h
@@ -139,6 +139,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_OBJECT "Object"
#define BLT_I18NCONTEXT_ID_PAINTCURVE "PaintCurve"
#define BLT_I18NCONTEXT_ID_PALETTE "Palette"
+#define BLT_I18NCONTEXT_ID_PARTICLESETTINGS "ParticleSettings"
#define BLT_I18NCONTEXT_ID_SCENE "Scene"
#define BLT_I18NCONTEXT_ID_SCREEN "Screen"
#define BLT_I18NCONTEXT_ID_SEQUENCE "Sequence"
@@ -192,6 +193,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_OBJECT, "id_object"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_PAINTCURVE, "id_paintcurve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_PALETTE, "id_palette"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_PARTICLESETTINGS, "id_particlesettings"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_SCENE, "id_scene"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_SCREEN, "id_screen"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_SEQUENCE, "id_sequence"), \
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 13bcff405ca..e312c4e0dcb 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -60,6 +60,7 @@ extern "C" {
#include "DNA_meta_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
+#include "DNA_particle_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
@@ -86,6 +87,7 @@ extern "C" {
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_rigidbody.h"
#include "BKE_sound.h"
#include "BKE_texture.h"
@@ -470,6 +472,11 @@ void DepsgraphNodeBuilder::build_object(Scene *scene, Base *base, Object *ob)
*/
build_animdata(&ob->id);
+ /* particle systems */
+ if (ob->particlesystem.first) {
+ build_particles(scene, ob);
+ }
+
/* grease pencil */
if (ob->gpd) {
build_gpencil(ob->gpd);
@@ -692,6 +699,50 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene)
}
}
+void DepsgraphNodeBuilder::build_particles(Scene *scene, Object *ob)
+{
+ /**
+ * Particle Systems Nodes
+ * ======================
+ *
+ * There are two types of nodes associated with representing
+ * particle systems:
+ * 1) Component (EVAL_PARTICLES) - This is the particle-system
+ * evaluation context for an object. It acts as the container
+ * for all the nodes associated with a particular set of particle
+ * systems.
+ * 2) Particle System Eval Operation - This operation node acts as a
+ * blackbox evaluation step for one particle system referenced by
+ * the particle systems stack. All dependencies link to this operation.
+ */
+
+ /* component for all particle systems */
+ ComponentDepsNode *psys_comp = add_component_node(&ob->id, DEPSNODE_TYPE_EVAL_PARTICLES);
+
+ /* particle systems */
+ LINKLIST_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
+ ParticleSettings *part = psys->part;
+
+ /* particle settings */
+ // XXX: what if this is used more than once!
+ build_animdata(&part->id);
+
+ /* this particle system */
+ // TODO: for now, this will just be a placeholder "ubereval" node
+ add_operation_node(psys_comp,
+ DEPSOP_TYPE_EXEC, function_bind(BKE_particle_system_eval,
+ _1,
+ scene,
+ ob,
+ psys),
+ DEG_OPCODE_PSYS_EVAL,
+ psys->name);
+ }
+
+ /* pointcache */
+ // TODO...
+}
+
/* Shapekeys */
void DepsgraphNodeBuilder::build_shapekeys(Key *key)
{
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 7d22ec79acc..b5272d3acf2 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -60,6 +60,7 @@ extern "C" {
#include "DNA_meta_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
+#include "DNA_particle_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
@@ -84,6 +85,7 @@ extern "C" {
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_rigidbody.h"
#include "BKE_sound.h"
#include "BKE_texture.h"
@@ -292,9 +294,9 @@ void DepsgraphRelationBuilder::add_collision_relations(const OperationKey &key,
MEM_freeN(collobjs);
}
-void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, EffectorWeights *eff, bool add_absorption, const char *name)
+void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name)
{
- ListBase *effectors = pdInitEffectors(scene, ob, eff, false);
+ ListBase *effectors = pdInitEffectors(scene, ob, psys, eff, false);
if (effectors) {
for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) {
@@ -303,6 +305,21 @@ void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key,
add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
}
+ if (eff->psys) {
+ if (eff->ob != ob) {
+ ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES);
+ add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
+
+ /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */
+ ComponentKey mod_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY);
+ add_relation(mod_key, key, DEPSREL_TYPE_STANDARD, name);
+ }
+ else if (eff->psys != psys) {
+ OperationKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PSYS_EVAL, eff->psys->name);
+ add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name);
+ }
+ }
+
if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
ComponentKey trf_key(&eff->pd->f_source->id, DEPSNODE_TYPE_TRANSFORM);
add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain");
@@ -465,6 +482,11 @@ void DepsgraphRelationBuilder::build_object(Main *bmain, Scene *scene, Object *o
}
}
+ /* particle systems */
+ if (ob->particlesystem.first) {
+ build_particles(scene, ob);
+ }
+
/* grease pencil */
if (ob->gpd) {
build_gpencil(&ob->id, ob->gpd);
@@ -1155,6 +1177,125 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
}
}
+void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob)
+{
+ TimeSourceKey time_src_key;
+ OperationKey obdata_ubereval_key(&ob->id,
+ DEPSNODE_TYPE_GEOMETRY,
+ DEG_OPCODE_GEOMETRY_UBEREVAL);
+
+ /* particle systems */
+ LINKLIST_FOREACH (ParticleSystem *, psys, &ob->particlesystem) {
+ ParticleSettings *part = psys->part;
+
+ /* particle settings */
+ build_animdata(&part->id);
+
+ /* this particle system */
+ OperationKey psys_key(&ob->id, DEPSNODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PSYS_EVAL, psys->name);
+
+ /* XXX: if particle system is later re-enabled, we must do full rebuild? */
+ if (!psys_check_enabled(ob, psys, G.is_rendering))
+ continue;
+
+ /* TODO(sergey): Are all particle systems depends on time?
+ * Hair without dynamics i.e.
+ */
+ add_relation(time_src_key, psys_key,
+ DEPSREL_TYPE_TIME,
+ "TimeSrc -> PSys");
+
+ /* TODO(sergey): Currently particle update is just a placeholder,
+ * hook it to the ubereval node so particle system is getting updated
+ * on playback.
+ */
+ add_relation(psys_key,
+ obdata_ubereval_key,
+ DEPSREL_TYPE_OPERATION,
+ "PSys -> UberEval");
+
+#if 0
+ if (ELEM(part->phystype, PART_PHYS_KEYED, PART_PHYS_BOIDS)) {
+ LINKLIST_FOREACH (ParticleTarget *, pt, &psys->targets) {
+ if (pt->ob && BLI_findlink(&pt->ob->particlesystem, pt->psys - 1)) {
+ node2 = dag_get_node(dag, pt->ob);
+ dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Targets");
+ }
+ }
+ }
+
+ if (part->ren_as == PART_DRAW_OB && part->dup_ob) {
+ node2 = dag_get_node(dag, part->dup_ob);
+ /* note that this relation actually runs in the wrong direction, the problem
+ * is that dupli system all have this (due to parenting), and the render
+ * engine instancing assumes particular ordering of objects in list */
+ dag_add_relation(dag, node, node2, DAG_RL_OB_OB, "Particle Object Visualization");
+ if (part->dup_ob->type == OB_MBALL)
+ dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Object Visualization");
+ }
+
+ if (part->ren_as == PART_DRAW_GR && part->dup_group) {
+ LINKLIST_FOREACH (GroupObject *, go, &part->dup_group->gobject) {
+ node2 = dag_get_node(dag, go->ob);
+ dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Particle Group Visualization");
+ }
+ }
+#endif
+
+ /* collisions */
+ if (part->type != PART_HAIR) {
+ add_collision_relations(psys_key, scene, ob, part->collision_group, ob->lay, true, "Particle Collision");
+ }
+ else if ((psys->flag & PSYS_HAIR_DYNAMICS) && psys->clmd && psys->clmd->coll_parms) {
+ add_collision_relations(psys_key, scene, ob, psys->clmd->coll_parms->group, ob->lay | scene->lay, true, "Hair Collision");
+ }
+
+ /* effectors */
+ add_forcefield_relations(psys_key, scene, ob, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field");
+
+ /* boids */
+ if (part->boids) {
+ LINKLIST_FOREACH (BoidState *, state, &part->boids->states) {
+ LINKLIST_FOREACH (BoidRule *, rule, &state->rules) {
+ Object *ruleob = NULL;
+ if (rule->type == eBoidRuleType_Avoid)
+ ruleob = ((BoidRuleGoalAvoid *)rule)->ob;
+ else if (rule->type == eBoidRuleType_FollowLeader)
+ ruleob = ((BoidRuleFollowLeader *)rule)->ob;
+
+ if (ruleob) {
+ ComponentKey ruleob_key(&ruleob->id, DEPSNODE_TYPE_TRANSFORM);
+ add_relation(ruleob_key, psys_key, DEPSREL_TYPE_TRANSFORM, "Boid Rule");
+ }
+ }
+ }
+ }
+
+ if (part->ren_as == PART_DRAW_OB && part->dup_ob) {
+ ComponentKey dup_ob_key(&part->dup_ob->id, DEPSNODE_TYPE_TRANSFORM);
+ add_relation(dup_ob_key,
+ psys_key,
+ DEPSREL_TYPE_TRANSFORM,
+ "Particle Object Visualization");
+ }
+ }
+
+ /* Particle depends on the object transform, so that channel is to be ready
+ * first.
+ *
+ * TODO(sergey): This relation should be altered once real granular update
+ * is implemented.
+ */
+ ComponentKey transform_key(&ob->id, DEPSNODE_TYPE_TRANSFORM);
+ add_relation(transform_key,
+ obdata_ubereval_key,
+ DEPSREL_TYPE_GEOMETRY_EVAL,
+ "Partcile Eval");
+
+ /* pointcache */
+ // TODO...
+}
+
/* Shapekeys */
void DepsgraphRelationBuilder::build_shapekeys(ID *obdata, Key *key)
{
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 2326e212feb..6e8485bee30 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -228,7 +228,7 @@ struct DepsgraphRelationBuilder
void build_movieclip(MovieClip *clip);
void add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name);
- void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, EffectorWeights *eff, bool add_absorption, const char *name);
+ void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name);
template <typename KeyType>
OperationDepsNode *find_operation_node(const KeyType &key);
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 171cd4529be..9952f714145 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -358,13 +358,22 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle,
int skip_forcefield,
const char *name)
{
- ListBase *effectors = pdInitEffectors(scene, ob, effector_weights, false);
+ ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false);
if (effectors) {
for (EffectorCache *eff = (EffectorCache*)effectors->first; eff; eff = eff->next) {
if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) {
DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_TRANSFORM, name);
+ if (eff->psys) {
+ DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_EVAL_PARTICLES, name);
+
+ /* TODO: remove this when/if EVAL_PARTICLES is sufficient
+ * for up to date particles.
+ */
+ DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_GEOMETRY, name);
+ }
+
if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) {
DEG_add_object_relation(handle,
eff->pd->f_source,
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 91eabbaf5d1..a9df68beb36 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -38,6 +38,7 @@ extern "C" {
#include "BLI_utildefines.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
@@ -117,7 +118,7 @@ void lib_id_recalc_tag_flag(Main *bmain, ID *id, int flag)
if (flag & OB_RECALC_OB)
lib_id_recalc_tag(bmain, id);
- if (flag & OB_RECALC_DATA)
+ if (flag & (OB_RECALC_DATA | PSYS_RECALC))
lib_id_recalc_data_tag(bmain, id);
}
else {
@@ -125,6 +126,33 @@ void lib_id_recalc_tag_flag(Main *bmain, ID *id, int flag)
}
}
+#ifdef DEPSGRAPH_USE_LEGACY_TAGGING
+void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag)
+{
+ if (flag) {
+ Object *object;
+ short idtype = GS(id->name);
+ if (idtype == ID_PA) {
+ ParticleSystem *psys;
+ for (object = (Object *)bmain->object.first;
+ object != NULL;
+ object = (Object *)object->id.next)
+ {
+ for (psys = (ParticleSystem *)object->particlesystem.first;
+ psys != NULL;
+ psys = (ParticleSystem *)psys->next)
+ {
+ if (&psys->part->id == id) {
+ DEG_id_tag_update_ex(bmain, &object->id, flag & OB_RECALC_ALL);
+ psys->recalc |= (flag & PSYS_RECALC);
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
} /* namespace */
/* Tag all nodes in ID-block for update.
@@ -214,6 +242,14 @@ void DEG_id_tag_update_ex(Main *bmain, ID *id, short flag)
}
}
}
+
+#ifdef DEPSGRAPH_USE_LEGACY_TAGGING
+ /* Special handling from the legacy depsgraph.
+ * TODO(sergey): Need to get rid of those once all the areas
+ * are re-formulated in terms of franular nodes.
+ */
+ depsgraph_legacy_handle_update_tag(bmain, id, flag);
+#endif
}
/* Tag given ID type for update. */
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index de6a17e9d41..f05932db1b2 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -43,6 +43,7 @@
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_screen_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
@@ -314,7 +315,7 @@ static short acf_generic_group_offset(bAnimContext *ac, bAnimListElem *ale)
offset += U.widget_unit;
}
/* materials and particles animdata */
- else if (GS(ale->id->name) == ID_MA)
+ else if (ELEM(GS(ale->id->name), ID_MA, ID_PA))
offset += (short)(0.7f * U.widget_unit);
/* if not in Action Editor mode, action-groups (and their children) must carry some offset too... */
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index ecaadd78dc2..117b8549712 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -124,6 +124,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
@@ -180,6 +181,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
@@ -281,6 +283,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
@@ -377,6 +380,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
@@ -2728,6 +2732,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 7bf146aa171..c12a050e9ba 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -64,6 +64,7 @@
#include "DNA_meta_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
+#include "DNA_particle_types.h"
#include "DNA_space_types.h"
#include "DNA_sequence_types.h"
#include "DNA_scene_types.h"
@@ -801,6 +802,19 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne
ale->adt = BKE_animdata_from_id(data);
break;
}
+ case ANIMTYPE_DSPART:
+ {
+ ParticleSettings *part = (ParticleSettings *)ale->data;
+ AnimData *adt = part->adt;
+
+ ale->flag = FILTER_PART_OBJD(part);
+
+ ale->key_data = (adt) ? adt->action : NULL;
+ ale->datatype = ALE_ACT;
+
+ ale->adt = BKE_animdata_from_id(data);
+ break;
+ }
case ANIMTYPE_DSTEX:
{
Tex *tex = (Tex *)data;
@@ -2073,6 +2087,12 @@ static size_t animdata_filter_ds_textures(bAnimContext *ac, ListBase *anim_data,
mtex = (MTex **)(&wo->mtex);
break;
}
+ case ID_PA:
+ {
+ ParticleSettings *part = (ParticleSettings *)owner_id;
+ mtex = (MTex **)(&part->mtex);
+ break;
+ }
default:
{
/* invalid/unsupported option */
@@ -2268,6 +2288,53 @@ static size_t animdata_filter_ds_modifiers(bAnimContext *ac, ListBase *anim_data
/* ............ */
+static size_t animdata_filter_ds_particles(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
+{
+ ParticleSystem *psys;
+ size_t items = 0;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ ListBase tmp_data = {NULL, NULL};
+ size_t tmp_items = 0;
+
+ /* if no material returned, skip - so that we don't get weird blank entries... */
+ if (ELEM(NULL, psys->part, psys->part->adt))
+ continue;
+
+ /* add particle-system's animation data to temp collection */
+ BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_PART_OBJD(psys->part))
+ {
+ /* particle system's animation data */
+ tmp_items += animfilter_block_data(ac, &tmp_data, ads, (ID *)psys->part, filter_mode);
+
+ /* textures */
+ if (!(ads->filterflag & ADS_FILTER_NOTEX))
+ tmp_items += animdata_filter_ds_textures(ac, &tmp_data, ads, (ID *)psys->part, filter_mode);
+ }
+ END_ANIMFILTER_SUBCHANNELS;
+
+ /* did we find anything? */
+ if (tmp_items) {
+ /* include particle-expand widget first */
+ if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
+ /* check if filtering by active status */
+ if (ANIMCHANNEL_ACTIVEOK(psys->part)) {
+ ANIMCHANNEL_NEW_CHANNEL(psys->part, ANIMTYPE_DSPART, psys->part);
+ }
+ }
+
+ /* now add the list of collected channels */
+ BLI_movelisttolist(anim_data, &tmp_data);
+ BLI_assert(BLI_listbase_is_empty(&tmp_data));
+ items += tmp_items;
+ }
+ }
+
+ /* return the number of items added to the list */
+ return items;
+}
+
+
static size_t animdata_filter_ds_obdata(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, Object *ob, int filter_mode)
{
ListBase tmp_data = {NULL, NULL};
@@ -2543,6 +2610,11 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data
tmp_items += animdata_filter_ds_obdata(ac, &tmp_data, ads, ob, filter_mode);
}
+ /* particles */
+ if ((ob->particlesystem.first) && !(ads->filterflag & ADS_FILTER_NOPART)) {
+ tmp_items += animdata_filter_ds_particles(ac, &tmp_data, ads, ob, filter_mode);
+ }
+
/* grease pencil */
if ((ob->gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) {
tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->gpd, filter_mode);
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 4ca7eaf0943..4a4ab832b28 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -161,6 +161,7 @@ typedef enum eAnim_ChannelType {
ANIMTYPE_DSSKEY,
ANIMTYPE_DSWOR,
ANIMTYPE_DSNTREE,
+ ANIMTYPE_DSPART,
ANIMTYPE_DSMBALL,
ANIMTYPE_DSARM,
ANIMTYPE_DSMESH,
@@ -278,6 +279,7 @@ typedef enum eAnimFilter_Flags {
#define FILTER_CAM_OBJD(ca) (CHECK_TYPE_INLINE(ca, Camera *), ((ca->flag & CAM_DS_EXPAND)))
#define FILTER_CACHEFILE_OBJD(cf) (CHECK_TYPE_INLINE(cf, CacheFile *), ((cf->flag & CACHEFILE_DS_EXPAND)))
#define FILTER_CUR_OBJD(cu) (CHECK_TYPE_INLINE(cu, Curve *), ((cu->flag & CU_DS_EXPAND)))
+#define FILTER_PART_OBJD(part) (CHECK_TYPE_INLINE(part, ParticleSettings *), ((part->flag & PART_DS_EXPAND)))
#define FILTER_MBALL_OBJD(mb) (CHECK_TYPE_INLINE(mb, MetaBall *), ((mb->flag2 & MB_DS_EXPAND)))
#define FILTER_ARM_OBJD(arm) (CHECK_TYPE_INLINE(arm, bArmature *), ((arm->flag & ARM_DS_EXPAND)))
#define FILTER_MESH_OBJD(me) (CHECK_TYPE_INLINE(me, Mesh *), ((me->flag & ME_DS_EXPAND)))
diff --git a/source/blender/editors/include/ED_buttons.h b/source/blender/editors/include/ED_buttons.h
index 636c583b828..64c16605dec 100644
--- a/source/blender/editors/include/ED_buttons.h
+++ b/source/blender/editors/include/ED_buttons.h
@@ -33,6 +33,7 @@
bool ED_texture_context_check_world(const struct bContext *C);
bool ED_texture_context_check_material(const struct bContext *C);
bool ED_texture_context_check_lamp(const struct bContext *C);
+bool ED_texture_context_check_particles(const struct bContext *C);
bool ED_texture_context_check_linestyle(const struct bContext *C);
bool ED_texture_context_check_others(const struct bContext *C);
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index f5c2b1da0cc..04ff5692717 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -190,6 +190,8 @@ bool ED_object_modifier_remove(struct ReportList *reports, struct Main *bmain,
void ED_object_modifier_clear(struct Main *bmain, struct Object *ob);
int ED_object_modifier_move_down(struct ReportList *reports, struct Object *ob, struct ModifierData *md);
int ED_object_modifier_move_up(struct ReportList *reports, struct Object *ob, struct ModifierData *md);
+int ED_object_modifier_convert(struct ReportList *reports, struct Main *bmain, struct Scene *scene,
+ struct Object *ob, struct ModifierData *md);
int ED_object_modifier_apply(struct ReportList *reports, struct Scene *scene,
struct Object *ob, struct ModifierData *md, int mode);
int ED_object_modifier_copy(struct ReportList *reports, struct Object *ob, struct ModifierData *md);
diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h
new file mode 100644
index 00000000000..6cb8c0cfb19
--- /dev/null
+++ b/source/blender/editors/include/ED_particle.h
@@ -0,0 +1,74 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ED_particle.h
+ * \ingroup editors
+ */
+
+#ifndef __ED_PARTICLE_H__
+#define __ED_PARTICLE_H__
+
+struct bContext;
+struct Object;
+struct ParticleEditSettings;
+struct rcti;
+struct PTCacheEdit;
+struct Scene;
+
+/* particle edit mode */
+void PE_free_ptcache_edit(struct PTCacheEdit *edit);
+int PE_start_edit(struct PTCacheEdit *edit);
+
+/* access */
+struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob);
+struct PTCacheEdit *PE_create_current(struct Scene *scene, struct Object *ob);
+void PE_current_changed(struct Scene *scene, struct Object *ob);
+int PE_minmax(struct Scene *scene, float min[3], float max[3]);
+struct ParticleEditSettings *PE_settings(struct Scene *scene);
+
+/* update calls */
+void PE_hide_keys_time(struct Scene *scene, struct PTCacheEdit *edit, float cfra);
+void PE_update_object(struct Scene *scene, struct Object *ob, int useflag);
+
+/* selection tools */
+int PE_mouse_particles(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+int PE_border_select(struct bContext *C, struct rcti *rect, bool select, bool extend);
+int PE_circle_select(struct bContext *C, int selecting, const int mval[2], float rad);
+int PE_lasso_select(struct bContext *C, const int mcords[][2], const short moves, bool extend, bool select);
+void PE_deselect_all_visible(struct PTCacheEdit *edit);
+
+/* undo */
+void PE_undo_push(struct Scene *scene, const char *str);
+void PE_undo_step(struct Scene *scene, int step);
+void PE_undo(struct Scene *scene);
+void PE_redo(struct Scene *scene);
+bool PE_undo_is_valid(struct Scene *scene);
+void PE_undo_number(struct Scene *scene, int nr);
+const char *PE_undo_get_name(struct Scene *scene, int nr, bool *r_active);
+
+#endif /* __ED_PARTICLE_H__ */
+
diff --git a/source/blender/editors/include/ED_physics.h b/source/blender/editors/include/ED_physics.h
index 511dae088a0..fed842c969e 100644
--- a/source/blender/editors/include/ED_physics.h
+++ b/source/blender/editors/include/ED_physics.h
@@ -39,6 +39,11 @@ struct wmKeyConfig;
struct Scene;
struct Object;
+/* particle_edit.c */
+int PE_poll(struct bContext *C);
+int PE_hair_poll(struct bContext *C);
+int PE_poll_view3d(struct bContext *C);
+
/* rigidbody_object.c */
bool ED_rigidbody_object_add(struct Main *bmain, struct Scene *scene, struct Object *ob, int type, struct ReportList *reports);
void ED_rigidbody_object_remove(struct Main *bmain, struct Scene *scene, struct Object *ob);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 3d22a26f34b..0573e8d9c94 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -1360,6 +1360,8 @@ int UI_idcode_icon_get(const int idcode)
return ICON_NODETREE;
case ID_OB:
return ICON_OBJECT_DATA;
+ case ID_PA:
+ return ICON_PARTICLE_DATA;
case ID_PAL:
return ICON_COLOR; /* TODO! this would need its own icon! */
case ID_PC:
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 9527ddc7088..2cdcf7a604d 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -64,6 +64,7 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_packedFile.h"
+#include "BKE_particle.h"
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_sca.h"
@@ -367,6 +368,7 @@ static const char *template_id_browse_tip(StructRNA *type)
case ID_AC: return N_("Browse Action to be linked");
case ID_NT: return N_("Browse Node Tree to be linked");
case ID_BR: return N_("Browse Brush to be linked");
+ case ID_PA: return N_("Browse Particle Settings to be linked");
case ID_GD: return N_("Browse Grease Pencil Data to be linked");
case ID_MC: return N_("Browse Movie Clip to be linked");
case ID_MSK: return N_("Browse Mask to be linked");
@@ -536,6 +538,7 @@ static void template_ID(
BLT_I18NCONTEXT_ID_ACTION,
BLT_I18NCONTEXT_ID_NODETREE,
BLT_I18NCONTEXT_ID_BRUSH,
+ BLT_I18NCONTEXT_ID_PARTICLESETTINGS,
BLT_I18NCONTEXT_ID_GPENCIL,
BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
);
@@ -803,6 +806,16 @@ static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v)
ED_undo_push(C, "Modifier convert to real");
}
+static int modifier_can_delete(ModifierData *md)
+{
+ /* fluid particle modifier can't be deleted here */
+ if (md->type == eModifierType_ParticleSystem)
+ if (((ParticleSystemModifierData *)md)->psys->part->type == PART_FLUID)
+ return 0;
+
+ return 1;
+}
+
/* Check whether Modifier is a simulation or not, this is used for switching to the physics/particles context tab */
static int modifier_is_simulation(ModifierData *md)
{
@@ -812,6 +825,10 @@ static int modifier_is_simulation(ModifierData *md)
{
return 1;
}
+ /* Particle Tab */
+ else if (md->type == eModifierType_ParticleSystem) {
+ return 2;
+ }
else {
return 0;
}
@@ -926,14 +943,18 @@ static uiLayout *draw_modifier(
UI_block_emboss_set(block, UI_EMBOSS_NONE);
/* When Modifier is a simulation, show button to switch to context rather than the delete button. */
- if (!modifier_is_simulation(md) ||
- STREQ(scene->r.engine, RE_engine_id_BLENDER_GAME))
+ if (modifier_can_delete(md) &&
+ (!modifier_is_simulation(md) ||
+ STREQ(scene->r.engine, RE_engine_id_BLENDER_GAME)))
{
uiItemO(row, "", ICON_X, "OBJECT_OT_modifier_remove");
}
else if (modifier_is_simulation(md) == 1) {
uiItemStringO(row, "", ICON_BUTS, "WM_OT_properties_context_change", "context", "PHYSICS");
}
+ else if (modifier_is_simulation(md) == 2) {
+ uiItemStringO(row, "", ICON_BUTS, "WM_OT_properties_context_change", "context", "PARTICLES");
+ }
UI_block_emboss_set(block, UI_EMBOSS);
}
@@ -948,20 +969,34 @@ static uiLayout *draw_modifier(
/* only here obdata, the rest of modifiers is ob level */
UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE);
- uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
- uiItemEnumO(row, "OBJECT_OT_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
- 0, "apply_as", MODIFIER_APPLY_DATA);
-
- if (modifier_isSameTopology(md) && !modifier_isNonGeometrical(md)) {
- uiItemEnumO(row, "OBJECT_OT_modifier_apply",
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply as Shape Key"),
- 0, "apply_as", MODIFIER_APPLY_SHAPE);
+ if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
+
+ if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
+ if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB))
+ uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), ICON_NONE,
+ "OBJECT_OT_duplicates_make_real");
+ else if (psys->part->ren_as == PART_DRAW_PATH && psys->pathcache)
+ uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), ICON_NONE,
+ "OBJECT_OT_modifier_convert");
+ }
+ }
+ else {
+ uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
+ uiItemEnumO(row, "OBJECT_OT_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
+ 0, "apply_as", MODIFIER_APPLY_DATA);
+
+ if (modifier_isSameTopology(md) && !modifier_isNonGeometrical(md)) {
+ uiItemEnumO(row, "OBJECT_OT_modifier_apply",
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply as Shape Key"),
+ 0, "apply_as", MODIFIER_APPLY_SHAPE);
+ }
}
UI_block_lock_clear(block);
UI_block_lock_set(block, ob && ID_IS_LINKED_DATABLOCK(ob), ERROR_LIBDATA_MESSAGE);
- if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody,
+ if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody, eModifierType_ParticleSystem,
eModifierType_Cloth, eModifierType_Smoke))
{
uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE,
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index baea0088e1f..f42dafd094c 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -83,6 +83,7 @@
#include "BKE_mesh.h"
#include "BKE_nla.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_report.h"
#include "BKE_sca.h"
#include "BKE_scene.h"
@@ -2021,6 +2022,24 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
}
}
}
+ if (dupflag & USER_DUP_PSYS) {
+ ParticleSystem *psys;
+ for (psys = obn->particlesystem.first; psys; psys = psys->next) {
+ id = (ID *) psys->part;
+ if (id) {
+ ID_NEW_REMAP_US(psys->part)
+ else {
+ psys->part = ID_NEW_SET(psys->part, BKE_particlesettings_copy(bmain, psys->part));
+ }
+
+ if (dupflag & USER_DUP_ACT) {
+ BKE_animdata_copy_id_action(&psys->part->id, true);
+ }
+
+ id_us_min(id);
+ }
+ }
+ }
id = obn->data;
didit = 0;
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 6b3284fe8b1..111afcdc7a7 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -75,6 +75,7 @@
#include "BKE_mball.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_pointcache.h"
#include "BKE_property.h"
#include "BKE_sca.h"
#include "BKE_softbody.h"
@@ -429,9 +430,22 @@ void ED_object_editmode_exit(bContext *C, int flag)
/* freedata only 0 now on file saves and render */
if (freedata) {
+ ListBase pidlist;
+ PTCacheID *pid;
+
/* for example; displist make is different in editmode */
scene->obedit = NULL; // XXX for context
+ /* flag object caches as outdated */
+ BKE_ptcache_ids_from_object(&pidlist, obedit, scene, 0);
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->type != PTCACHE_TYPE_PARTICLES) /* particles don't need reset on geometry change */
+ pid->cache->flag |= PTCACHE_OUTDATED;
+ }
+ BLI_freelistN(&pidlist);
+
+ BKE_ptcache_object_reset(scene, obedit, PTCACHE_RESET_OUTDATED);
+
/* also flush ob recalc, doesn't take much overhead, but used for particles */
DAG_id_tag_update(&obedit->id, OB_RECALC_OB | OB_RECALC_DATA);
@@ -1569,9 +1583,13 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED(
ob = CTX_data_active_object(C);
if (ob) {
+ const bool use_mode_particle_edit = (BLI_listbase_is_empty(&ob->particlesystem) == false) ||
+ (ob->soft != NULL) ||
+ (modifiers_findByType(ob, eModifierType_Cloth) != NULL);
while (input->identifier) {
if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) ||
(input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) ||
+ (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) ||
(ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT,
OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) ||
(input->value == OB_MODE_OBJECT))
@@ -1613,6 +1631,8 @@ static const char *object_mode_op_string(int mode)
return "PAINT_OT_weight_paint_toggle";
if (mode == OB_MODE_TEXTURE_PAINT)
return "PAINT_OT_texture_paint_toggle";
+ if (mode == OB_MODE_PARTICLE_EDIT)
+ return "PARTICLE_OT_particle_edit_toggle";
if (mode == OB_MODE_POSE)
return "OBJECT_OT_posemode_toggle";
if (mode == OB_MODE_GPENCIL)
@@ -1634,7 +1654,7 @@ static bool object_mode_compat_test(Object *ob, ObjectMode mode)
switch (ob->type) {
case OB_MESH:
if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
- OB_MODE_TEXTURE_PAINT))
+ OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT))
{
return true;
}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 3a6e585d4c5..9710e4f843d 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -169,6 +169,7 @@ void OBJECT_OT_modifier_remove(struct wmOperatorType *ot);
void OBJECT_OT_modifier_move_up(struct wmOperatorType *ot);
void OBJECT_OT_modifier_move_down(struct wmOperatorType *ot);
void OBJECT_OT_modifier_apply(struct wmOperatorType *ot);
+void OBJECT_OT_modifier_convert(struct wmOperatorType *ot);
void OBJECT_OT_modifier_copy(struct wmOperatorType *ot);
void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot);
void OBJECT_OT_multires_reshape(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 9175bd69a28..b44ddf925a8 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -41,7 +41,6 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_force.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_bitmap.h"
@@ -72,6 +71,7 @@
#include "BKE_object_deform.h"
#include "BKE_ocean.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
#include "BKE_softbody.h"
#include "BKE_editmesh.h"
@@ -111,56 +111,64 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc
}
}
- /* get new modifier data to add */
- new_md = modifier_new(type);
-
- if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
- md = ob->modifiers.first;
-
- while (md && modifierType_getInfo(md->type)->type == eModifierTypeType_OnlyDeform)
- md = md->next;
-
- BLI_insertlinkbefore(&ob->modifiers, md, new_md);
+ if (type == eModifierType_ParticleSystem) {
+ /* don't need to worry about the new modifier's name, since that is set to the number
+ * of particle systems which shouldn't have too many duplicates
+ */
+ new_md = object_add_particle_system(scene, ob, name);
}
- else
- BLI_addtail(&ob->modifiers, new_md);
+ else {
+ /* get new modifier data to add */
+ new_md = modifier_new(type);
+
+ if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
+ md = ob->modifiers.first;
+
+ while (md && modifierType_getInfo(md->type)->type == eModifierTypeType_OnlyDeform)
+ md = md->next;
+
+ BLI_insertlinkbefore(&ob->modifiers, md, new_md);
+ }
+ else
+ BLI_addtail(&ob->modifiers, new_md);
- if (name) {
- BLI_strncpy_utf8(new_md->name, name, sizeof(new_md->name));
- }
+ if (name) {
+ BLI_strncpy_utf8(new_md->name, name, sizeof(new_md->name));
+ }
- /* make sure modifier data has unique name */
+ /* make sure modifier data has unique name */
- modifier_unique_name(&ob->modifiers, new_md);
-
- /* special cases */
- if (type == eModifierType_Softbody) {
- if (!ob->soft) {
- ob->soft = sbNew(scene);
- ob->softflag |= OB_SB_GOAL | OB_SB_EDGES;
- }
- }
- else if (type == eModifierType_Collision) {
- if (!ob->pd)
- ob->pd = object_add_collision_fields(0);
+ modifier_unique_name(&ob->modifiers, new_md);
- ob->pd->deflect = 1;
- }
- else if (type == eModifierType_Surface) {
- /* pass */
- }
- else if (type == eModifierType_Multires) {
- /* set totlvl from existing MDISPS layer if object already had it */
- multiresModifier_set_levels_from_disps((MultiresModifierData *)new_md, ob);
+ /* special cases */
+ if (type == eModifierType_Softbody) {
+ if (!ob->soft) {
+ ob->soft = sbNew(scene);
+ ob->softflag |= OB_SB_GOAL | OB_SB_EDGES;
+ }
+ }
+ else if (type == eModifierType_Collision) {
+ if (!ob->pd)
+ ob->pd = object_add_collision_fields(0);
+
+ ob->pd->deflect = 1;
+ }
+ else if (type == eModifierType_Surface) {
+ /* pass */
+ }
+ else if (type == eModifierType_Multires) {
+ /* set totlvl from existing MDISPS layer if object already had it */
+ multiresModifier_set_levels_from_disps((MultiresModifierData *)new_md, ob);
- if (ob->mode & OB_MODE_SCULPT) {
- /* ensure that grid paint mask layer is created */
- BKE_sculpt_mask_layers_ensure(ob, (MultiresModifierData *)new_md);
+ if (ob->mode & OB_MODE_SCULPT) {
+ /* ensure that grid paint mask layer is created */
+ BKE_sculpt_mask_layers_ensure(ob, (MultiresModifierData *)new_md);
+ }
+ }
+ else if (type == eModifierType_Skin) {
+ /* ensure skin-node customdata exists */
+ BKE_mesh_ensure_skin_customdata(ob->data);
}
- }
- else if (type == eModifierType_Skin) {
- /* ensure skin-node customdata exists */
- BKE_mesh_ensure_skin_customdata(ob->data);
}
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
@@ -272,7 +280,14 @@ static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md,
}
/* special cases */
- if (md->type == eModifierType_Softbody) {
+ if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
+
+ BLI_remlink(&ob->particlesystem, psmd->psys);
+ psys_free(ob, psmd->psys);
+ psmd->psys = NULL;
+ }
+ else if (md->type == eModifierType_Softbody) {
if (ob->soft) {
sbFree(ob->soft);
ob->soft = NULL;
@@ -299,6 +314,12 @@ static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md,
modifier_skin_customdata_delete(ob);
}
+ if (ELEM(md->type, eModifierType_Softbody, eModifierType_Cloth) &&
+ BLI_listbase_is_empty(&ob->particlesystem))
+ {
+ ob->mode &= ~OB_MODE_PARTICLE_EDIT;
+ }
+
DAG_relations_tag_update(bmain);
BLI_remlink(&ob->modifiers, md);
@@ -390,6 +411,115 @@ int ED_object_modifier_move_down(ReportList *reports, Object *ob, ModifierData *
return 1;
}
+int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene *scene, Object *ob, ModifierData *md)
+{
+ Object *obn;
+ ParticleSystem *psys;
+ ParticleCacheKey *key, **cache;
+ ParticleSettings *part;
+ Mesh *me;
+ MVert *mvert;
+ MEdge *medge;
+ int a, k, kmax;
+ int totvert = 0, totedge = 0, cvert = 0;
+ int totpart = 0, totchild = 0;
+
+ if (md->type != eModifierType_ParticleSystem) return 0;
+ if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) return 0;
+
+ psys = ((ParticleSystemModifierData *)md)->psys;
+ part = psys->part;
+
+ if (part->ren_as != PART_DRAW_PATH || psys->pathcache == NULL)
+ return 0;
+
+ totpart = psys->totcached;
+ totchild = psys->totchildcache;
+
+ if (totchild && (part->draw & PART_DRAW_PARENT) == 0)
+ totpart = 0;
+
+ /* count */
+ cache = psys->pathcache;
+ for (a = 0; a < totpart; a++) {
+ key = cache[a];
+
+ if (key->segments > 0) {
+ totvert += key->segments + 1;
+ totedge += key->segments;
+ }
+ }
+
+ cache = psys->childcache;
+ for (a = 0; a < totchild; a++) {
+ key = cache[a];
+
+ if (key->segments > 0) {
+ totvert += key->segments + 1;
+ totedge += key->segments;
+ }
+ }
+
+ if (totvert == 0) return 0;
+
+ /* add new mesh */
+ obn = BKE_object_add(bmain, scene, OB_MESH, NULL);
+ me = obn->data;
+
+ me->totvert = totvert;
+ me->totedge = totedge;
+
+ me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
+ me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, totedge);
+ me->mface = CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, 0);
+
+ mvert = me->mvert;
+ medge = me->medge;
+
+ /* copy coordinates */
+ cache = psys->pathcache;
+ for (a = 0; a < totpart; a++) {
+ key = cache[a];
+ kmax = key->segments;
+ for (k = 0; k <= kmax; k++, key++, cvert++, mvert++) {
+ copy_v3_v3(mvert->co, key->co);
+ if (k) {
+ medge->v1 = cvert - 1;
+ medge->v2 = cvert;
+ medge->flag = ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
+ medge++;
+ }
+ else {
+ /* cheap trick to select the roots */
+ mvert->flag |= SELECT;
+ }
+ }
+ }
+
+ cache = psys->childcache;
+ for (a = 0; a < totchild; a++) {
+ key = cache[a];
+ kmax = key->segments;
+ for (k = 0; k <= kmax; k++, key++, cvert++, mvert++) {
+ copy_v3_v3(mvert->co, key->co);
+ if (k) {
+ medge->v1 = cvert - 1;
+ medge->v2 = cvert;
+ medge->flag = ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
+ medge++;
+ }
+ else {
+ /* cheap trick to select the roots */
+ mvert->flag |= SELECT;
+ }
+ }
+ }
+
+ DAG_relations_tag_update(bmain);
+
+ return 1;
+}
+
static int modifier_apply_shape(ReportList *reports, Scene *scene, Object *ob, ModifierData *md)
{
const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
@@ -520,6 +650,20 @@ static int modifier_apply_obdata(ReportList *reports, Scene *scene, Object *ob,
return 0;
}
+ /* lattice modifier can be applied to particle system too */
+ if (ob->particlesystem.first) {
+
+ ParticleSystem *psys = ob->particlesystem.first;
+
+ for (; psys; psys = psys->next) {
+
+ if (psys->part->type != PART_HAIR)
+ continue;
+
+ psys_apply_hair_lattice(scene, ob, psys);
+ }
+ }
+
return 1;
}
@@ -728,13 +872,21 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
static int modifier_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
+ int mode_orig = ob->mode;
if (!md || !ED_object_modifier_remove(op->reports, bmain, ob, md))
return OPERATOR_CANCELLED;
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+
+ /* if cloth/softbody was removed, particle mode could be cleared */
+ if (mode_orig & OB_MODE_PARTICLE_EDIT)
+ if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0)
+ if (scene->basact && scene->basact->object == ob)
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
return OPERATOR_FINISHED;
}
@@ -890,6 +1042,47 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
edit_modifier_properties(ot);
}
+/************************ convert modifier operator *********************/
+
+static int modifier_convert_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = ED_object_active_context(C);
+ ModifierData *md = edit_modifier_property_get(op, ob, 0);
+
+ if (!md || !ED_object_modifier_convert(op->reports, bmain, scene, ob, md))
+ return OPERATOR_CANCELLED;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int modifier_convert_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ if (edit_modifier_invoke_properties(C, op))
+ return modifier_convert_exec(C, op);
+ else
+ return OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_modifier_convert(wmOperatorType *ot)
+{
+ ot->name = "Convert Modifier";
+ ot->description = "Convert particles to a mesh object";
+ ot->idname = "OBJECT_OT_modifier_convert";
+
+ ot->invoke = modifier_convert_invoke;
+ ot->exec = modifier_convert_exec;
+ ot->poll = edit_modifier_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+ edit_modifier_properties(ot);
+}
+
/************************ copy modifier operator *********************/
static int modifier_copy_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 4837ca50105..7e7e1ef182c 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -133,6 +133,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_modifier_move_up);
WM_operatortype_append(OBJECT_OT_modifier_move_down);
WM_operatortype_append(OBJECT_OT_modifier_apply);
+ WM_operatortype_append(OBJECT_OT_modifier_convert);
WM_operatortype_append(OBJECT_OT_modifier_copy);
WM_operatortype_append(OBJECT_OT_multires_subdivide);
WM_operatortype_append(OBJECT_OT_multires_reshape);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 088ade6963d..d30022c01f8 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -43,6 +43,7 @@
#include "DNA_lattice_types.h"
#include "DNA_material_types.h"
#include "DNA_meta_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_world_types.h"
#include "DNA_object_types.h"
@@ -2214,6 +2215,7 @@ static int make_local_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
AnimData *adt;
+ ParticleSystem *psys;
Material *ma, ***matarar;
Lamp *la;
ID *id;
@@ -2280,6 +2282,9 @@ static int make_local_exec(bContext *C, wmOperator *op)
}
}
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ id_make_local(bmain, &psys->part->id, false, false);
+
adt = BKE_animdata_from_id(&ob->id);
if (adt) BKE_animdata_make_local(adt);
}
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index 7db32957b42..f1b7186f8a1 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -37,7 +37,6 @@
#include "DNA_group_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
-#include "DNA_object_types.h"
#include "DNA_property_types.h"
#include "DNA_scene_types.h"
#include "DNA_armature_types.h"
@@ -54,6 +53,7 @@
#include "BKE_group.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_particle.h"
#include "BKE_property.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -192,6 +192,7 @@ enum {
OBJECT_SELECT_LINKED_MATERIAL,
OBJECT_SELECT_LINKED_TEXTURE,
OBJECT_SELECT_LINKED_DUPGROUP,
+ OBJECT_SELECT_LINKED_PARTICLE,
OBJECT_SELECT_LINKED_LIBRARY,
OBJECT_SELECT_LINKED_LIBRARY_OBDATA
};
@@ -202,6 +203,7 @@ static EnumPropertyItem prop_select_linked_types[] = {
{OBJECT_SELECT_LINKED_MATERIAL, "MATERIAL", 0, "Material", ""},
{OBJECT_SELECT_LINKED_TEXTURE, "TEXTURE", 0, "Texture", ""},
{OBJECT_SELECT_LINKED_DUPGROUP, "DUPGROUP", 0, "Dupligroup", ""},
+ {OBJECT_SELECT_LINKED_PARTICLE, "PARTICLE", 0, "Particle System", ""},
{OBJECT_SELECT_LINKED_LIBRARY, "LIBRARY", 0, "Library", ""},
{OBJECT_SELECT_LINKED_LIBRARY_OBDATA, "LIBRARY_OBDATA", 0, "Library (Object Data)", ""},
{0, NULL, 0, NULL, NULL}
@@ -311,6 +313,37 @@ static bool object_select_all_by_dup_group(bContext *C, Object *ob)
return changed;
}
+static bool object_select_all_by_particle(bContext *C, Object *ob)
+{
+ ParticleSystem *psys_act = psys_get_current(ob);
+ bool changed = false;
+
+ CTX_DATA_BEGIN (C, Base *, base, visible_bases)
+ {
+ if ((base->flag & SELECT) == 0) {
+ /* loop through other particles*/
+ ParticleSystem *psys;
+
+ for (psys = base->object->particlesystem.first; psys; psys = psys->next) {
+ if (psys->part == psys_act->part) {
+ base->flag |= SELECT;
+ changed = true;
+ break;
+ }
+
+ if (base->flag & SELECT) {
+ break;
+ }
+ }
+
+ base->object->flag = base->flag;
+ }
+ }
+ CTX_DATA_END;
+
+ return changed;
+}
+
static bool object_select_all_by_library(bContext *C, Library *lib)
{
bool changed = false;
@@ -428,6 +461,12 @@ static int object_select_linked_exec(bContext *C, wmOperator *op)
changed = object_select_all_by_dup_group(C, ob);
}
+ else if (nr == OBJECT_SELECT_LINKED_PARTICLE) {
+ if (BLI_listbase_is_empty(&ob->particlesystem))
+ return OPERATOR_CANCELLED;
+
+ changed = object_select_all_by_particle(C, ob);
+ }
else if (nr == OBJECT_SELECT_LINKED_LIBRARY) {
/* do nothing */
changed = object_select_all_by_library(C, ob->id.lib);
diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt
index e4513c14413..898422dac51 100644
--- a/source/blender/editors/physics/CMakeLists.txt
+++ b/source/blender/editors/physics/CMakeLists.txt
@@ -38,8 +38,12 @@ set(INC_SYS
set(SRC
dynamicpaint_ops.c
+ particle_boids.c
+ particle_edit.c
+ particle_object.c
physics_fluid.c
physics_ops.c
+ physics_pointcache.c
rigidbody_constraint.c
rigidbody_object.c
rigidbody_world.c
diff --git a/source/blender/editors/physics/particle_boids.c b/source/blender/editors/physics/particle_boids.c
new file mode 100644
index 00000000000..14b12497c4a
--- /dev/null
+++ b/source/blender/editors/physics/particle_boids.c
@@ -0,0 +1,371 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 Janne Karhu.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/physics/particle_boids.c
+ * \ingroup edphys
+ */
+
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_particle_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_boids.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_main.h"
+#include "BKE_particle.h"
+
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "physics_intern.h"
+
+/************************ add/del boid rule operators *********************/
+static int rule_add_exec(bContext *C, wmOperator *op)
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ int type= RNA_enum_get(op->ptr, "type");
+
+ BoidRule *rule;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ state = boid_get_current_state(part->boids);
+
+ for (rule=state->rules.first; rule; rule=rule->next)
+ rule->flag &= ~BOIDRULE_CURRENT;
+
+ rule = boid_new_rule(type);
+ rule->flag |= BOIDRULE_CURRENT;
+
+ BLI_addtail(&state->rules, rule);
+
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA|PSYS_RECALC_RESET);
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_rule_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Boid Rule";
+ ot->description = "Add a boid rule to the current boid state";
+ ot->idname = "BOID_OT_rule_add";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = rule_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_boidrule_type_items, 0, "Type", "");
+}
+static int rule_del_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidRule *rule;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ state = boid_get_current_state(part->boids);
+
+ for (rule=state->rules.first; rule; rule=rule->next) {
+ if (rule->flag & BOIDRULE_CURRENT) {
+ BLI_remlink(&state->rules, rule);
+ MEM_freeN(rule);
+ break;
+ }
+ }
+ rule = state->rules.first;
+
+ if (rule)
+ rule->flag |= BOIDRULE_CURRENT;
+
+ DAG_relations_tag_update(bmain);
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA|PSYS_RECALC_RESET);
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_rule_del(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Boid Rule";
+ ot->idname = "BOID_OT_rule_del";
+ ot->description = "Delete current boid rule";
+
+ /* api callbacks */
+ ot->exec = rule_del_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move up/down boid rule operators *********************/
+static int rule_move_up_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidRule *rule;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ state = boid_get_current_state(part->boids);
+ for (rule = state->rules.first; rule; rule=rule->next) {
+ if (rule->flag & BOIDRULE_CURRENT && rule->prev) {
+ BLI_remlink(&state->rules, rule);
+ BLI_insertlinkbefore(&state->rules, rule->prev, rule);
+
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA|PSYS_RECALC_RESET);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_rule_move_up(wmOperatorType *ot)
+{
+ ot->name = "Move Up Boid Rule";
+ ot->description = "Move boid rule up in the list";
+ ot->idname = "BOID_OT_rule_move_up";
+
+ ot->exec = rule_move_up_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int rule_move_down_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidRule *rule;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ state = boid_get_current_state(part->boids);
+ for (rule = state->rules.first; rule; rule=rule->next) {
+ if (rule->flag & BOIDRULE_CURRENT && rule->next) {
+ BLI_remlink(&state->rules, rule);
+ BLI_insertlinkafter(&state->rules, rule->next, rule);
+
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA|PSYS_RECALC_RESET);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_rule_move_down(wmOperatorType *ot)
+{
+ ot->name = "Move Down Boid Rule";
+ ot->description = "Move boid rule down in the list";
+ ot->idname = "BOID_OT_rule_move_down";
+
+ ot->exec = rule_move_down_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+
+/************************ add/del boid state operators *********************/
+static int state_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ for (state=part->boids->states.first; state; state=state->next)
+ state->flag &= ~BOIDSTATE_CURRENT;
+
+ state = boid_new_state(part->boids);
+ state->flag |= BOIDSTATE_CURRENT;
+
+ BLI_addtail(&part->boids->states, state);
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_state_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Boid State";
+ ot->description = "Add a boid state to the particle system";
+ ot->idname = "BOID_OT_state_add";
+
+ /* api callbacks */
+ ot->exec = state_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+static int state_del_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ for (state=part->boids->states.first; state; state=state->next) {
+ if (state->flag & BOIDSTATE_CURRENT) {
+ BLI_remlink(&part->boids->states, state);
+ MEM_freeN(state);
+ break;
+ }
+ }
+
+ /* there must be at least one state */
+ if (!part->boids->states.first) {
+ state = boid_new_state(part->boids);
+ BLI_addtail(&part->boids->states, state);
+ }
+ else
+ state = part->boids->states.first;
+
+ state->flag |= BOIDSTATE_CURRENT;
+
+ DAG_relations_tag_update(bmain);
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA|PSYS_RECALC_RESET);
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_state_del(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Boid State";
+ ot->idname = "BOID_OT_state_del";
+ ot->description = "Delete current boid state";
+
+ /* api callbacks */
+ ot->exec = state_del_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move up/down boid state operators *********************/
+static int state_move_up_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidSettings *boids;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ boids = part->boids;
+
+ for (state = boids->states.first; state; state=state->next) {
+ if (state->flag & BOIDSTATE_CURRENT && state->prev) {
+ BLI_remlink(&boids->states, state);
+ BLI_insertlinkbefore(&boids->states, state->prev, state);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_state_move_up(wmOperatorType *ot)
+{
+ ot->name = "Move Up Boid State";
+ ot->description = "Move boid state up in the list";
+ ot->idname = "BOID_OT_state_move_up";
+
+ ot->exec = state_move_up_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int state_move_down_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", &RNA_ParticleSettings);
+ ParticleSettings *part = ptr.data;
+ BoidSettings *boids;
+ BoidState *state;
+
+ if (!part || part->phystype != PART_PHYS_BOIDS)
+ return OPERATOR_CANCELLED;
+
+ boids = part->boids;
+
+ for (state = boids->states.first; state; state=state->next) {
+ if (state->flag & BOIDSTATE_CURRENT && state->next) {
+ BLI_remlink(&boids->states, state);
+ BLI_insertlinkafter(&boids->states, state->next, state);
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA|PSYS_RECALC_RESET);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void BOID_OT_state_move_down(wmOperatorType *ot)
+{
+ ot->name = "Move Down Boid State";
+ ot->description = "Move boid state down in the list";
+ ot->idname = "BOID_OT_state_move_down";
+
+ ot->exec = state_move_down_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
new file mode 100644
index 00000000000..e22a145b3a6
--- /dev/null
+++ b/source/blender/editors/physics/particle_edit.c
@@ -0,0 +1,4923 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/physics/particle_edit.c
+ * \ingroup edphys
+ */
+
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_math.h"
+#include "BLI_lasso.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_kdtree.h"
+#include "BLI_rand.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_object.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_report.h"
+#include "BKE_bvhutils.h"
+#include "BKE_pointcache.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "ED_object.h"
+#include "ED_physics.h"
+#include "ED_mesh.h"
+#include "ED_particle.h"
+#include "ED_view3d.h"
+
+#include "UI_resources.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "physics_intern.h"
+
+void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
+void PTCacheUndo_clear(PTCacheEdit *edit);
+void recalc_lengths(PTCacheEdit *edit);
+void recalc_emitter_field(Object *ob, ParticleSystem *psys);
+void update_world_cos(Object *ob, PTCacheEdit *edit);
+
+#define KEY_K PTCacheEditKey *key; int k
+#define POINT_P PTCacheEditPoint *point; int p
+#define LOOP_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++)
+#define LOOP_VISIBLE_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!(point->flag & PEP_HIDE))
+#define LOOP_SELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point_is_selected(point))
+#define LOOP_UNSELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!point_is_selected(point))
+#define LOOP_EDITED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_EDIT_RECALC)
+#define LOOP_TAGGED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_TAG)
+#define LOOP_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++)
+#define LOOP_VISIBLE_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (!(key->flag & PEK_HIDE))
+#define LOOP_SELECTED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if ((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
+#define LOOP_TAGGED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (key->flag & PEK_TAG)
+
+#define KEY_WCO ((key->flag & PEK_USE_WCO) ? key->world_co : key->co)
+
+/**************************** utilities *******************************/
+
+int PE_poll(bContext *C)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+
+ if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
+ return 0;
+
+ return (PE_get_current(scene, ob) != NULL);
+}
+
+int PE_hair_poll(bContext *C)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit;
+
+ if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
+ return 0;
+
+ edit= PE_get_current(scene, ob);
+
+ return (edit && edit->psys);
+}
+
+int PE_poll_view3d(bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ return (PE_poll(C) &&
+ (sa && sa->spacetype == SPACE_VIEW3D) &&
+ (ar && ar->regiontype == RGN_TYPE_WINDOW));
+}
+
+void PE_free_ptcache_edit(PTCacheEdit *edit)
+{
+ POINT_P;
+
+ if (edit==0) return;
+
+ PTCacheUndo_clear(edit);
+
+ if (edit->points) {
+ LOOP_POINTS {
+ if (point->keys)
+ MEM_freeN(point->keys);
+ }
+
+ MEM_freeN(edit->points);
+ }
+
+ if (edit->mirror_cache)
+ MEM_freeN(edit->mirror_cache);
+
+ if (edit->emitter_cosnos) {
+ MEM_freeN(edit->emitter_cosnos);
+ edit->emitter_cosnos= 0;
+ }
+
+ if (edit->emitter_field) {
+ BLI_kdtree_free(edit->emitter_field);
+ edit->emitter_field= 0;
+ }
+
+ psys_free_path_cache(edit->psys, edit);
+
+ MEM_freeN(edit);
+}
+
+/************************************************/
+/* Edit Mode Helpers */
+/************************************************/
+
+int PE_start_edit(PTCacheEdit *edit)
+{
+ if (edit) {
+ edit->edited = 1;
+ if (edit->psys)
+ edit->psys->flag |= PSYS_EDITED;
+ return 1;
+ }
+
+ return 0;
+}
+
+ParticleEditSettings *PE_settings(Scene *scene)
+{
+ return scene->toolsettings ? &scene->toolsettings->particle : NULL;
+}
+
+static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *brush)
+{
+ // here we can enable unified brush size, needs more work...
+ // UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+ // float size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size;
+
+ return brush->size * U.pixelsize;
+}
+
+
+/* always gets at least the first particlesystem even if PSYS_CURRENT flag is not set
+ *
+ * note: this function runs on poll, therefor it can runs many times a second
+ * keep it fast! */
+static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
+{
+ ParticleEditSettings *pset= PE_settings(scene);
+ PTCacheEdit *edit = NULL;
+ ListBase pidlist;
+ PTCacheID *pid;
+
+ if (pset==NULL || ob==NULL)
+ return NULL;
+
+ pset->scene = scene;
+ pset->object = ob;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ /* in the case of only one editable thing, set pset->edittype accordingly */
+ if (BLI_listbase_is_single(&pidlist)) {
+ pid = pidlist.first;
+ switch (pid->type) {
+ case PTCACHE_TYPE_PARTICLES:
+ pset->edittype = PE_TYPE_PARTICLES;
+ break;
+ case PTCACHE_TYPE_SOFTBODY:
+ pset->edittype = PE_TYPE_SOFTBODY;
+ break;
+ case PTCACHE_TYPE_CLOTH:
+ pset->edittype = PE_TYPE_CLOTH;
+ break;
+ }
+ }
+
+ for (pid=pidlist.first; pid; pid=pid->next) {
+ if (pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
+ ParticleSystem *psys = pid->calldata;
+
+ if (psys->flag & PSYS_CURRENT) {
+ if (psys->part && psys->part->type == PART_HAIR) {
+ if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
+ if (create && !psys->pointcache->edit)
+ PE_create_particle_edit(scene, ob, pid->cache, NULL);
+ edit = pid->cache->edit;
+ }
+ else {
+ if (create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
+ PE_create_particle_edit(scene, ob, NULL, psys);
+ edit = psys->edit;
+ }
+ }
+ else {
+ if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
+ PE_create_particle_edit(scene, ob, pid->cache, psys);
+ edit = pid->cache->edit;
+ }
+
+ break;
+ }
+ }
+ else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
+ if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
+ pset->flag |= PE_FADE_TIME;
+ // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
+ PE_create_particle_edit(scene, ob, pid->cache, NULL);
+ }
+ edit = pid->cache->edit;
+ break;
+ }
+ else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
+ if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
+ pset->flag |= PE_FADE_TIME;
+ // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
+ PE_create_particle_edit(scene, ob, pid->cache, NULL);
+ }
+ edit = pid->cache->edit;
+ break;
+ }
+ }
+
+ if (edit)
+ edit->pid = *pid;
+
+ BLI_freelistN(&pidlist);
+
+ return edit;
+}
+
+PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
+{
+ return pe_get_current(scene, ob, 0);
+}
+
+PTCacheEdit *PE_create_current(Scene *scene, Object *ob)
+{
+ return pe_get_current(scene, ob, 1);
+}
+
+void PE_current_changed(Scene *scene, Object *ob)
+{
+ if (ob->mode == OB_MODE_PARTICLE_EDIT)
+ PE_create_current(scene, ob);
+}
+
+void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
+{
+ ParticleEditSettings *pset=PE_settings(scene);
+ POINT_P; KEY_K;
+
+
+ if (pset->flag & PE_FADE_TIME && pset->selectmode==SCE_SELECT_POINT) {
+ LOOP_POINTS {
+ LOOP_KEYS {
+ if (fabsf(cfra - *key->time) < pset->fade_frames)
+ key->flag &= ~PEK_HIDE;
+ else {
+ key->flag |= PEK_HIDE;
+ //key->flag &= ~PEK_SELECT;
+ }
+ }
+ }
+ }
+ else {
+ LOOP_POINTS {
+ LOOP_KEYS {
+ key->flag &= ~PEK_HIDE;
+ }
+ }
+ }
+}
+
+static int pe_x_mirror(Object *ob)
+{
+ if (ob->type == OB_MESH)
+ return (((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X);
+
+ return 0;
+}
+
+/****************** common struct passed to callbacks ******************/
+
+typedef struct PEData {
+ ViewContext vc;
+ bglMats mats;
+
+ Scene *scene;
+ Object *ob;
+ DerivedMesh *dm;
+ PTCacheEdit *edit;
+ BVHTreeFromMesh shape_bvh;
+
+ const int *mval;
+ rcti *rect;
+ float rad;
+ float dist;
+ float dval;
+ int select;
+
+ float *dvec;
+ float combfac;
+ float pufffac;
+ float cutfac;
+ float smoothfac;
+ float weightfac;
+ float growfac;
+ int totrekey;
+
+ int invert;
+ int tot;
+ float vec[3];
+
+ int select_action;
+ int select_toggle_action;
+} PEData;
+
+static void PE_set_data(bContext *C, PEData *data)
+{
+ memset(data, 0, sizeof(*data));
+
+ data->scene= CTX_data_scene(C);
+ data->ob= CTX_data_active_object(C);
+ data->edit= PE_get_current(data->scene, data->ob);
+}
+
+static void PE_set_view3d_data(bContext *C, PEData *data)
+{
+ PE_set_data(C, data);
+
+ view3d_set_viewcontext(C, &data->vc);
+ /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather than (obmat * viewmat) */
+ view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);
+
+ if (V3D_IS_ZBUF(data->vc.v3d)) {
+ if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
+ /* needed or else the draw matrix can be incorrect */
+ view3d_operator_needs_opengl(C);
+
+ ED_view3d_backbuf_validate(&data->vc);
+ /* we may need to force an update here by setting the rv3d as dirty
+ * for now it seems ok, but take care!:
+ * rv3d->depths->dirty = 1; */
+ ED_view3d_depth_update(data->vc.ar);
+ }
+ }
+}
+
+static bool PE_create_shape_tree(PEData *data, Object *shapeob)
+{
+ DerivedMesh *dm = shapeob->derivedFinal;
+
+ memset(&data->shape_bvh, 0, sizeof(data->shape_bvh));
+
+ if (!dm) {
+ return false;
+ }
+
+ DM_ensure_looptri(dm);
+ return (bvhtree_from_mesh_looptri(&data->shape_bvh, dm, 0.0f, 4, 8) != NULL);
+}
+
+static void PE_free_shape_tree(PEData *data)
+{
+ free_bvhtree_from_mesh(&data->shape_bvh);
+}
+
+/*************************** selection utilities *******************************/
+
+static bool key_test_depth(PEData *data, const float co[3], const int screen_co[2])
+{
+ View3D *v3d= data->vc.v3d;
+ ViewDepths *vd = data->vc.rv3d->depths;
+ double ux, uy, uz;
+ float depth;
+
+ /* nothing to do */
+ if (!V3D_IS_ZBUF(v3d))
+ return true;
+
+ /* used to calculate here but all callers have the screen_co already, so pass as arg */
+#if 0
+ if (ED_view3d_project_int_global(data->vc.ar, co, screen_co,
+ V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
+ {
+ return 0;
+ }
+#endif
+
+ gluProject(co[0], co[1], co[2], data->mats.modelview, data->mats.projection,
+ (GLint *)data->mats.viewport, &ux, &uy, &uz);
+
+ /* check if screen_co is within bounds because brush_cut uses out of screen coords */
+ if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
+ BLI_assert(vd && vd->depths);
+ /* we know its not clipped */
+ depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
+ }
+ else
+ return 0;
+
+ if ((float)uz - 0.00001f > depth)
+ return 0;
+ else
+ return 1;
+}
+
+static bool key_inside_circle(PEData *data, float rad, const float co[3], float *distance)
+{
+ float dx, dy, dist;
+ int screen_co[2];
+
+ /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
+ if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
+ return 0;
+ }
+
+ dx= data->mval[0] - screen_co[0];
+ dy= data->mval[1] - screen_co[1];
+ dist = sqrtf(dx * dx + dy * dy);
+
+ if (dist > rad)
+ return 0;
+
+ if (key_test_depth(data, co, screen_co)) {
+ if (distance)
+ *distance=dist;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool key_inside_rect(PEData *data, const float co[3])
+{
+ int screen_co[2];
+
+ if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
+ return 0;
+ }
+
+ if (screen_co[0] > data->rect->xmin && screen_co[0] < data->rect->xmax &&
+ screen_co[1] > data->rect->ymin && screen_co[1] < data->rect->ymax)
+ {
+ return key_test_depth(data, co, screen_co);
+ }
+
+ return 0;
+}
+
+static bool key_inside_test(PEData *data, const float co[3])
+{
+ if (data->mval)
+ return key_inside_circle(data, data->rad, co, NULL);
+ else
+ return key_inside_rect(data, co);
+}
+
+static bool point_is_selected(PTCacheEditPoint *point)
+{
+ KEY_K;
+
+ if (point->flag & PEP_HIDE)
+ return 0;
+
+ LOOP_SELECTED_KEYS {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*************************** iterators *******************************/
+
+typedef void (*ForPointFunc)(PEData *data, int point_index);
+typedef void (*ForKeyFunc)(PEData *data, int point_index, int key_index);
+typedef void (*ForKeyMatFunc)(PEData *data, float mat[4][4], float imat[4][4], int point_index, int key_index, PTCacheEditKey *key);
+
+static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, int nearest)
+{
+ ParticleEditSettings *pset= PE_settings(data->scene);
+ PTCacheEdit *edit= data->edit;
+ POINT_P; KEY_K;
+ int nearest_point, nearest_key;
+ float dist= data->rad;
+
+ /* in path select mode we have no keys */
+ if (pset->selectmode==SCE_SELECT_PATH)
+ return;
+
+ nearest_point= -1;
+ nearest_key= -1;
+
+ LOOP_VISIBLE_POINTS {
+ if (pset->selectmode == SCE_SELECT_END) {
+ if (point->totkey) {
+ /* only do end keys */
+ key= point->keys + point->totkey-1;
+
+ if (nearest) {
+ if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
+ nearest_point= p;
+ nearest_key= point->totkey-1;
+ }
+ }
+ else if (key_inside_test(data, KEY_WCO))
+ func(data, p, point->totkey-1);
+ }
+ }
+ else {
+ /* do all keys */
+ LOOP_VISIBLE_KEYS {
+ if (nearest) {
+ if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
+ nearest_point= p;
+ nearest_key= k;
+ }
+ }
+ else if (key_inside_test(data, KEY_WCO))
+ func(data, p, k);
+ }
+ }
+ }
+
+ /* do nearest only */
+ if (nearest && nearest_point > -1)
+ func(data, nearest_point, nearest_key);
+}
+
+static void foreach_mouse_hit_point(PEData *data, ForPointFunc func, int selected)
+{
+ ParticleEditSettings *pset= PE_settings(data->scene);
+ PTCacheEdit *edit= data->edit;
+ POINT_P; KEY_K;
+
+ /* all is selected in path mode */
+ if (pset->selectmode==SCE_SELECT_PATH)
+ selected=0;
+
+ LOOP_VISIBLE_POINTS {
+ if (pset->selectmode==SCE_SELECT_END) {
+ if (point->totkey) {
+ /* only do end keys */
+ key= point->keys + point->totkey - 1;
+
+ if (selected==0 || key->flag & PEK_SELECT)
+ if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
+ func(data, p);
+ }
+ }
+ else {
+ /* do all keys */
+ LOOP_VISIBLE_KEYS {
+ if (selected==0 || key->flag & PEK_SELECT) {
+ if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
+ func(data, p);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected)
+{
+ PTCacheEdit *edit = data->edit;
+ ParticleSystem *psys = edit->psys;
+ ParticleSystemModifierData *psmd = NULL;
+ ParticleEditSettings *pset= PE_settings(data->scene);
+ POINT_P; KEY_K;
+ float mat[4][4], imat[4][4];
+
+ unit_m4(mat);
+ unit_m4(imat);
+
+ if (edit->psys)
+ psmd= psys_get_modifier(data->ob, edit->psys);
+
+ /* all is selected in path mode */
+ if (pset->selectmode==SCE_SELECT_PATH)
+ selected= 0;
+
+ LOOP_VISIBLE_POINTS {
+ if (pset->selectmode==SCE_SELECT_END) {
+ if (point->totkey) {
+ /* only do end keys */
+ key= point->keys + point->totkey-1;
+
+ if (selected==0 || key->flag & PEK_SELECT) {
+ if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
+ if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
+ psys_mat_hair_to_global(data->ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
+ invert_m4_m4(imat, mat);
+ }
+
+ func(data, mat, imat, p, point->totkey-1, key);
+ }
+ }
+ }
+ }
+ else {
+ /* do all keys */
+ LOOP_VISIBLE_KEYS {
+ if (selected==0 || key->flag & PEK_SELECT) {
+ if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
+ if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
+ psys_mat_hair_to_global(data->ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
+ invert_m4_m4(imat, mat);
+ }
+
+ func(data, mat, imat, p, k, key);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void foreach_selected_point(PEData *data, ForPointFunc func)
+{
+ PTCacheEdit *edit = data->edit;
+ POINT_P;
+
+ LOOP_SELECTED_POINTS {
+ func(data, p);
+ }
+}
+
+static void foreach_selected_key(PEData *data, ForKeyFunc func)
+{
+ PTCacheEdit *edit = data->edit;
+ POINT_P; KEY_K;
+
+ LOOP_VISIBLE_POINTS {
+ LOOP_SELECTED_KEYS {
+ func(data, p, k);
+ }
+ }
+}
+
+static void foreach_point(PEData *data, ForPointFunc func)
+{
+ PTCacheEdit *edit = data->edit;
+ POINT_P;
+
+ LOOP_POINTS {
+ func(data, p);
+ }
+}
+
+static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
+{
+ ParticleEditSettings *pset= PE_settings(scene);
+ POINT_P; KEY_K;
+ int sel= 0;
+
+ LOOP_VISIBLE_POINTS {
+ if (pset->selectmode==SCE_SELECT_POINT) {
+ LOOP_SELECTED_KEYS {
+ sel++;
+ }
+ }
+ else if (pset->selectmode==SCE_SELECT_END) {
+ if (point->totkey) {
+ key = point->keys + point->totkey - 1;
+ if (key->flag & PEK_SELECT)
+ sel++;
+ }
+ }
+ }
+
+ return sel;
+}
+
+/************************************************/
+/* Particle Edit Mirroring */
+/************************************************/
+
+static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
+{
+ PTCacheEdit *edit;
+ ParticleSystemModifierData *psmd;
+ KDTree *tree;
+ KDTreeNearest nearest;
+ HairKey *key;
+ PARTICLE_P;
+ float mat[4][4], co[3];
+ int index, totpart;
+
+ edit= psys->edit;
+ psmd= psys_get_modifier(ob, psys);
+ totpart= psys->totpart;
+
+ if (!psmd->dm_final)
+ return;
+
+ tree= BLI_kdtree_new(totpart);
+
+ /* insert particles into kd tree */
+ LOOP_PARTICLES {
+ key = pa->hair;
+ psys_mat_hair_to_orco(ob, psmd->dm_final, psys->part->from, pa, mat);
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ BLI_kdtree_insert(tree, p, co);
+ }
+
+ BLI_kdtree_balance(tree);
+
+ /* lookup particles and set in mirror cache */
+ if (!edit->mirror_cache)
+ edit->mirror_cache= MEM_callocN(sizeof(int)*totpart, "PE mirror cache");
+
+ LOOP_PARTICLES {
+ key = pa->hair;
+ psys_mat_hair_to_orco(ob, psmd->dm_final, psys->part->from, pa, mat);
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ co[0] = -co[0];
+
+ index= BLI_kdtree_find_nearest(tree, co, &nearest);
+
+ /* this needs a custom threshold still, duplicated for editmode mirror */
+ if (index != -1 && index != p && (nearest.dist <= 0.0002f))
+ edit->mirror_cache[p] = index;
+ else
+ edit->mirror_cache[p] = -1;
+ }
+
+ /* make sure mirrors are in two directions */
+ LOOP_PARTICLES {
+ if (edit->mirror_cache[p]) {
+ index= edit->mirror_cache[p];
+ if (edit->mirror_cache[index] != p)
+ edit->mirror_cache[p] = -1;
+ }
+ }
+
+ BLI_kdtree_free(tree);
+}
+
+static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
+{
+ HairKey *hkey, *mhkey;
+ PTCacheEditPoint *point, *mpoint;
+ PTCacheEditKey *key, *mkey;
+ PTCacheEdit *edit;
+ float mat[4][4], mmat[4][4], immat[4][4];
+ int i, mi, k;
+
+ edit= psys->edit;
+ i= pa - psys->particles;
+
+ /* find mirrored particle if needed */
+ if (!mpa) {
+ if (!edit->mirror_cache)
+ PE_update_mirror_cache(ob, psys);
+
+ if (!edit->mirror_cache)
+ return; /* something went wrong! */
+
+ mi= edit->mirror_cache[i];
+ if (mi == -1)
+ return;
+ mpa= psys->particles + mi;
+ }
+ else
+ mi= mpa - psys->particles;
+
+ point = edit->points + i;
+ mpoint = edit->points + mi;
+
+ /* make sure they have the same amount of keys */
+ if (pa->totkey != mpa->totkey) {
+ if (mpa->hair) MEM_freeN(mpa->hair);
+ if (mpoint->keys) MEM_freeN(mpoint->keys);
+
+ mpa->hair= MEM_dupallocN(pa->hair);
+ mpa->totkey= pa->totkey;
+ mpoint->keys= MEM_dupallocN(point->keys);
+ mpoint->totkey= point->totkey;
+
+ mhkey= mpa->hair;
+ mkey= mpoint->keys;
+ for (k=0; k<mpa->totkey; k++, mkey++, mhkey++) {
+ mkey->co= mhkey->co;
+ mkey->time= &mhkey->time;
+ mkey->flag &= ~PEK_SELECT;
+ }
+ }
+
+ /* mirror positions and tags */
+ psys_mat_hair_to_orco(ob, dm, psys->part->from, pa, mat);
+ psys_mat_hair_to_orco(ob, dm, psys->part->from, mpa, mmat);
+ invert_m4_m4(immat, mmat);
+
+ hkey=pa->hair;
+ mhkey=mpa->hair;
+ key= point->keys;
+ mkey= mpoint->keys;
+ for (k=0; k<pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
+ copy_v3_v3(mhkey->co, hkey->co);
+ mul_m4_v3(mat, mhkey->co);
+ mhkey->co[0] = -mhkey->co[0];
+ mul_m4_v3(immat, mhkey->co);
+
+ if (key->flag & PEK_TAG)
+ mkey->flag |= PEK_TAG;
+
+ mkey->length = key->length;
+ }
+
+ if (point->flag & PEP_TAG)
+ mpoint->flag |= PEP_TAG;
+ if (point->flag & PEP_EDIT_RECALC)
+ mpoint->flag |= PEP_EDIT_RECALC;
+}
+
+static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
+{
+ PTCacheEdit *edit;
+ ParticleSystemModifierData *psmd;
+ POINT_P;
+
+ if (!psys)
+ return;
+
+ edit= psys->edit;
+ psmd= psys_get_modifier(ob, psys);
+
+ if (!psmd->dm_final)
+ return;
+
+ if (!edit->mirror_cache)
+ PE_update_mirror_cache(ob, psys);
+
+ if (!edit->mirror_cache)
+ return; /* something went wrong */
+
+ /* we delay settings the PARS_EDIT_RECALC for mirrored particles
+ * to avoid doing mirror twice */
+ LOOP_POINTS {
+ if (point->flag & PEP_EDIT_RECALC) {
+ PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);
+
+ if (edit->mirror_cache[p] != -1)
+ edit->points[edit->mirror_cache[p]].flag &= ~PEP_EDIT_RECALC;
+ }
+ }
+
+ LOOP_POINTS {
+ if (point->flag & PEP_EDIT_RECALC)
+ if (edit->mirror_cache[p] != -1)
+ edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC;
+ }
+}
+
+/************************************************/
+/* Edit Calculation */
+/************************************************/
+/* tries to stop edited particles from going through the emitter's surface */
+static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
+{
+ ParticleEditSettings *pset= PE_settings(scene);
+ ParticleSystem *psys;
+ ParticleSystemModifierData *psmd;
+ POINT_P; KEY_K;
+ int index;
+ float *vec, *nor, dvec[3], dot, dist_1st=0.0f;
+ float hairimat[4][4], hairmat[4][4];
+ const float dist = ED_view3d_select_dist_px() * 0.01f;
+
+ if (edit==NULL || edit->psys==NULL || (pset->flag & PE_DEFLECT_EMITTER)==0 || (edit->psys->flag & PSYS_GLOBAL_HAIR))
+ return;
+
+ psys = edit->psys;
+ psmd = psys_get_modifier(ob, psys);
+
+ if (!psmd->dm_final)
+ return;
+
+ LOOP_EDITED_POINTS {
+ psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles + p, hairmat);
+
+ LOOP_KEYS {
+ mul_m4_v3(hairmat, key->co);
+ }
+
+ LOOP_KEYS {
+ if (k==0) {
+ dist_1st = len_v3v3((key+1)->co, key->co);
+ dist_1st *= dist * pset->emitterdist;
+ }
+ else {
+ index= BLI_kdtree_find_nearest(edit->emitter_field, key->co, NULL);
+
+ vec=edit->emitter_cosnos +index*6;
+ nor=vec+3;
+
+ sub_v3_v3v3(dvec, key->co, vec);
+
+ dot=dot_v3v3(dvec, nor);
+ copy_v3_v3(dvec, nor);
+
+ if (dot>0.0f) {
+ if (dot<dist_1st) {
+ normalize_v3(dvec);
+ mul_v3_fl(dvec, dist_1st-dot);
+ add_v3_v3(key->co, dvec);
+ }
+ }
+ else {
+ normalize_v3(dvec);
+ mul_v3_fl(dvec, dist_1st-dot);
+ add_v3_v3(key->co, dvec);
+ }
+ if (k==1)
+ dist_1st*=1.3333f;
+ }
+ }
+
+ invert_m4_m4(hairimat, hairmat);
+
+ LOOP_KEYS {
+ mul_m4_v3(hairimat, key->co);
+ }
+ }
+}
+/* force set distances between neighboring keys */
+static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
+{
+
+ ParticleEditSettings *pset=PE_settings(scene);
+ POINT_P; KEY_K;
+ float dv1[3];
+
+ if (edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
+ return;
+
+ if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
+ return;
+
+ LOOP_EDITED_POINTS {
+ LOOP_KEYS {
+ if (k) {
+ sub_v3_v3v3(dv1, key->co, (key - 1)->co);
+ normalize_v3(dv1);
+ mul_v3_fl(dv1, (key - 1)->length);
+ add_v3_v3v3(key->co, (key - 1)->co, dv1);
+ }
+ }
+ }
+}
+/* try to find a nice solution to keep distances between neighboring keys */
+static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
+{
+ ParticleEditSettings *pset=PE_settings(scene);
+ POINT_P;
+ PTCacheEditKey *key;
+ int j, k;
+ float tlen;
+ float dv0[3] = {0.0f, 0.0f, 0.0f};
+ float dv1[3] = {0.0f, 0.0f, 0.0f};
+ float dv2[3] = {0.0f, 0.0f, 0.0f};
+
+ if (edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
+ return;
+
+ if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
+ return;
+
+ LOOP_EDITED_POINTS {
+ for (j=1; j<point->totkey; j++) {
+ float mul= 1.0f / (float)point->totkey;
+
+ if (pset->flag & PE_LOCK_FIRST) {
+ key= point->keys + 1;
+ k= 1;
+ dv1[0] = dv1[1] = dv1[2] = 0.0;
+ }
+ else {
+ key= point->keys;
+ k= 0;
+ dv0[0] = dv0[1] = dv0[2] = 0.0;
+ }
+
+ for (; k<point->totkey; k++, key++) {
+ if (k) {
+ sub_v3_v3v3(dv0, (key - 1)->co, key->co);
+ tlen= normalize_v3(dv0);
+ mul_v3_fl(dv0, (mul * (tlen - (key - 1)->length)));
+ }
+
+ if (k < point->totkey - 1) {
+ sub_v3_v3v3(dv2, (key + 1)->co, key->co);
+ tlen= normalize_v3(dv2);
+ mul_v3_fl(dv2, mul * (tlen - key->length));
+ }
+
+ if (k) {
+ add_v3_v3((key-1)->co, dv1);
+ }
+
+ add_v3_v3v3(dv1, dv0, dv2);
+ }
+ }
+ }
+}
+/* set current distances to be kept between neighbouting keys */
+void recalc_lengths(PTCacheEdit *edit)
+{
+ POINT_P; KEY_K;
+
+ if (edit==0)
+ return;
+
+ LOOP_EDITED_POINTS {
+ key= point->keys;
+ for (k=0; k<point->totkey-1; k++, key++) {
+ key->length= len_v3v3(key->co, (key + 1)->co);
+ }
+ }
+}
+
+/* calculate a tree for finding nearest emitter's vertice */
+void recalc_emitter_field(Object *ob, ParticleSystem *psys)
+{
+ DerivedMesh *dm=psys_get_modifier(ob, psys)->dm_final;
+ PTCacheEdit *edit= psys->edit;
+ float *vec, *nor;
+ int i, totface /*, totvert*/;
+
+ if (!dm)
+ return;
+
+ if (edit->emitter_cosnos)
+ MEM_freeN(edit->emitter_cosnos);
+
+ BLI_kdtree_free(edit->emitter_field);
+
+ totface=dm->getNumTessFaces(dm);
+ /*totvert=dm->getNumVerts(dm);*/ /*UNSUED*/
+
+ edit->emitter_cosnos=MEM_callocN(totface*6*sizeof(float), "emitter cosnos");
+
+ edit->emitter_field= BLI_kdtree_new(totface);
+
+ vec=edit->emitter_cosnos;
+ nor=vec+3;
+
+ for (i=0; i<totface; i++, vec+=6, nor+=6) {
+ MFace *mface=dm->getTessFaceData(dm, i, CD_MFACE);
+ MVert *mvert;
+
+ mvert=dm->getVertData(dm, mface->v1, CD_MVERT);
+ copy_v3_v3(vec, mvert->co);
+ VECCOPY(nor, mvert->no);
+
+ mvert=dm->getVertData(dm, mface->v2, CD_MVERT);
+ add_v3_v3v3(vec, vec, mvert->co);
+ VECADD(nor, nor, mvert->no);
+
+ mvert=dm->getVertData(dm, mface->v3, CD_MVERT);
+ add_v3_v3v3(vec, vec, mvert->co);
+ VECADD(nor, nor, mvert->no);
+
+ if (mface->v4) {
+ mvert=dm->getVertData(dm, mface->v4, CD_MVERT);
+ add_v3_v3v3(vec, vec, mvert->co);
+ VECADD(nor, nor, mvert->no);
+
+ mul_v3_fl(vec, 0.25);
+ }
+ else
+ mul_v3_fl(vec, 1.0f / 3.0f);
+
+ normalize_v3(nor);
+
+ BLI_kdtree_insert(edit->emitter_field, i, vec);
+ }
+
+ BLI_kdtree_balance(edit->emitter_field);
+}
+
+static void PE_update_selection(Scene *scene, Object *ob, int useflag)
+{
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ HairKey *hkey;
+ POINT_P; KEY_K;
+
+ /* flag all particles to be updated if not using flag */
+ if (!useflag)
+ LOOP_POINTS
+ point->flag |= PEP_EDIT_RECALC;
+
+ /* flush edit key flag to hair key flag to preserve selection
+ * on save */
+ if (edit->psys) LOOP_POINTS {
+ hkey = edit->psys->particles[p].hair;
+ LOOP_KEYS {
+ hkey->editflag= key->flag;
+ hkey++;
+ }
+ }
+
+ psys_cache_edit_paths(scene, ob, edit, CFRA, G.is_rendering);
+
+
+ /* disable update flag */
+ LOOP_POINTS
+ point->flag &= ~PEP_EDIT_RECALC;
+}
+
+void update_world_cos(Object *ob, PTCacheEdit *edit)
+{
+ ParticleSystem *psys = edit->psys;
+ ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys);
+ POINT_P; KEY_K;
+ float hairmat[4][4];
+
+ if (psys==0 || psys->edit==0 || psmd->dm_final==NULL)
+ return;
+
+ LOOP_POINTS {
+ if (!(psys->flag & PSYS_GLOBAL_HAIR))
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles+p, hairmat);
+
+ LOOP_KEYS {
+ copy_v3_v3(key->world_co, key->co);
+ if (!(psys->flag & PSYS_GLOBAL_HAIR))
+ mul_m4_v3(hairmat, key->world_co);
+ }
+ }
+}
+static void update_velocities(PTCacheEdit *edit)
+{
+ /*TODO: get frs_sec properly */
+ float vec1[3], vec2[3], frs_sec, dfra;
+ POINT_P; KEY_K;
+
+ /* hair doesn't use velocities */
+ if (edit->psys || !edit->points || !edit->points->keys->vel)
+ return;
+
+ frs_sec = edit->pid.flag & PTCACHE_VEL_PER_SEC ? 25.0f : 1.0f;
+
+ LOOP_EDITED_POINTS {
+ LOOP_KEYS {
+ if (k==0) {
+ dfra = *(key+1)->time - *key->time;
+
+ if (dfra <= 0.0f)
+ continue;
+
+ sub_v3_v3v3(key->vel, (key+1)->co, key->co);
+
+ if (point->totkey>2) {
+ sub_v3_v3v3(vec1, (key+1)->co, (key+2)->co);
+ project_v3_v3v3(vec2, vec1, key->vel);
+ sub_v3_v3v3(vec2, vec1, vec2);
+ madd_v3_v3fl(key->vel, vec2, 0.5f);
+ }
+ }
+ else if (k==point->totkey-1) {
+ dfra = *key->time - *(key-1)->time;
+
+ if (dfra <= 0.0f)
+ continue;
+
+ sub_v3_v3v3(key->vel, key->co, (key-1)->co);
+
+ if (point->totkey>2) {
+ sub_v3_v3v3(vec1, (key-2)->co, (key-1)->co);
+ project_v3_v3v3(vec2, vec1, key->vel);
+ sub_v3_v3v3(vec2, vec1, vec2);
+ madd_v3_v3fl(key->vel, vec2, 0.5f);
+ }
+ }
+ else {
+ dfra = *(key+1)->time - *(key-1)->time;
+
+ if (dfra <= 0.0f)
+ continue;
+
+ sub_v3_v3v3(key->vel, (key+1)->co, (key-1)->co);
+ }
+ mul_v3_fl(key->vel, frs_sec/dfra);
+ }
+ }
+}
+
+void PE_update_object(Scene *scene, Object *ob, int useflag)
+{
+ /* use this to do partial particle updates, not usable when adding or
+ * removing, then a full redo is necessary and calling this may crash */
+ ParticleEditSettings *pset= PE_settings(scene);
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ POINT_P;
+
+ if (!edit)
+ return;
+
+ /* flag all particles to be updated if not using flag */
+ if (!useflag)
+ LOOP_POINTS {
+ point->flag |= PEP_EDIT_RECALC;
+ }
+
+ /* do post process on particle edit keys */
+ pe_iterate_lengths(scene, edit);
+ pe_deflect_emitter(scene, ob, edit);
+ PE_apply_lengths(scene, edit);
+ if (pe_x_mirror(ob))
+ PE_apply_mirror(ob, edit->psys);
+ if (edit->psys)
+ update_world_cos(ob, edit);
+ if (pset->flag & PE_AUTO_VELOCITY)
+ update_velocities(edit);
+ PE_hide_keys_time(scene, edit, CFRA);
+
+ /* regenerate path caches */
+ psys_cache_edit_paths(scene, ob, edit, CFRA, G.is_rendering);
+
+ /* disable update flag */
+ LOOP_POINTS {
+ point->flag &= ~PEP_EDIT_RECALC;
+ }
+
+ if (edit->psys)
+ edit->psys->flag &= ~PSYS_HAIR_UPDATED;
+}
+
+/************************************************/
+/* Edit Selections */
+/************************************************/
+
+/*-----selection callbacks-----*/
+
+static void select_key(PEData *data, int point_index, int key_index)
+{
+ PTCacheEdit *edit = data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ PTCacheEditKey *key = point->keys + key_index;
+
+ if (data->select)
+ key->flag |= PEK_SELECT;
+ else
+ key->flag &= ~PEK_SELECT;
+
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static void select_keys(PEData *data, int point_index, int UNUSED(key_index))
+{
+ PTCacheEdit *edit = data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ KEY_K;
+
+ LOOP_KEYS {
+ if (data->select)
+ key->flag |= PEK_SELECT;
+ else
+ key->flag &= ~PEK_SELECT;
+ }
+
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static void extend_key_select(PEData *data, int point_index, int key_index)
+{
+ PTCacheEdit *edit = data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ PTCacheEditKey *key = point->keys + key_index;
+
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static void deselect_key_select(PEData *data, int point_index, int key_index)
+{
+ PTCacheEdit *edit = data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ PTCacheEditKey *key = point->keys + key_index;
+
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static void toggle_key_select(PEData *data, int point_index, int key_index)
+{
+ PTCacheEdit *edit = data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ PTCacheEditKey *key = point->keys + key_index;
+
+ key->flag ^= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+/************************ de select all operator ************************/
+
+static void select_action_apply(PTCacheEditPoint *point, PTCacheEditKey *key, int action)
+{
+ switch (action) {
+ case SEL_SELECT:
+ if ((key->flag & PEK_SELECT) == 0) {
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ break;
+ case SEL_DESELECT:
+ if (key->flag & PEK_SELECT) {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ break;
+ case SEL_INVERT:
+ if ((key->flag & PEK_SELECT) == 0) {
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ else {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ break;
+ }
+}
+
+static int pe_select_all_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ POINT_P; KEY_K;
+ int action = RNA_enum_get(op->ptr, "action");
+
+ if (action == SEL_TOGGLE) {
+ action = SEL_SELECT;
+ LOOP_VISIBLE_POINTS {
+ LOOP_SELECTED_KEYS {
+ action = SEL_DESELECT;
+ break;
+ }
+
+ if (action == SEL_DESELECT)
+ break;
+ }
+ }
+
+ LOOP_VISIBLE_POINTS {
+ LOOP_VISIBLE_KEYS {
+ select_action_apply(point, key, action);
+ }
+ }
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_select_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "(De)select All";
+ ot->idname = "PARTICLE_OT_select_all";
+ ot->description = "(De)select all particles' keys";
+
+ /* api callbacks */
+ ot->exec = pe_select_all_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
+/************************ pick select operator ************************/
+
+int PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+{
+ PEData data;
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ POINT_P; KEY_K;
+
+ if (!PE_start_edit(edit))
+ return OPERATOR_CANCELLED;
+
+ if (!extend && !deselect && !toggle) {
+ LOOP_VISIBLE_POINTS {
+ LOOP_SELECTED_KEYS {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+ }
+
+ PE_set_view3d_data(C, &data);
+ data.mval= mval;
+ data.rad = ED_view3d_select_dist_px();
+
+ /* 1 = nearest only */
+ if (extend)
+ for_mouse_hit_keys(&data, extend_key_select, 1);
+ else if (deselect)
+ for_mouse_hit_keys(&data, deselect_key_select, 1);
+ else
+ for_mouse_hit_keys(&data, toggle_key_select, 1);
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/************************ select root operator ************************/
+
+static void select_root(PEData *data, int point_index)
+{
+ PTCacheEditPoint *point = data->edit->points + point_index;
+ PTCacheEditKey *key = point->keys;
+
+ if (point->flag & PEP_HIDE)
+ return;
+
+ if (data->select_action != SEL_TOGGLE)
+ select_action_apply(point, key, data->select_action);
+ else if (key->flag & PEK_SELECT)
+ data->select_toggle_action = SEL_DESELECT;
+}
+
+static int select_roots_exec(bContext *C, wmOperator *op)
+{
+ PEData data;
+ int action = RNA_enum_get(op->ptr, "action");
+
+ PE_set_data(C, &data);
+
+ if (action == SEL_TOGGLE) {
+ data.select_action = SEL_TOGGLE;
+ data.select_toggle_action = SEL_SELECT;
+
+ foreach_point(&data, select_root);
+
+ action = data.select_toggle_action;
+ }
+
+ data.select_action = action;
+ foreach_point(&data, select_root);
+
+ PE_update_selection(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_select_roots(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Roots";
+ ot->idname = "PARTICLE_OT_select_roots";
+ ot->description = "Select roots of all visible particles";
+
+ /* api callbacks */
+ ot->exec = select_roots_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_select_action(ot, SEL_SELECT);
+}
+
+/************************ select tip operator ************************/
+
+static void select_tip(PEData *data, int point_index)
+{
+ PTCacheEditPoint *point = data->edit->points + point_index;
+ PTCacheEditKey *key;
+
+ if (point->totkey == 0) {
+ return;
+ }
+
+ key = &point->keys[point->totkey - 1];
+
+ if (point->flag & PEP_HIDE)
+ return;
+
+ if (data->select_action != SEL_TOGGLE)
+ select_action_apply(point, key, data->select_action);
+ else if (key->flag & PEK_SELECT)
+ data->select_toggle_action = SEL_DESELECT;
+}
+
+static int select_tips_exec(bContext *C, wmOperator *op)
+{
+ PEData data;
+ int action = RNA_enum_get(op->ptr, "action");
+
+ PE_set_data(C, &data);
+
+ if (action == SEL_TOGGLE) {
+ data.select_action = SEL_TOGGLE;
+ data.select_toggle_action = SEL_SELECT;
+
+ foreach_point(&data, select_tip);
+
+ action = data.select_toggle_action;
+ }
+
+ data.select_action = action;
+ foreach_point(&data, select_tip);
+
+ PE_update_selection(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_select_tips(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Tips";
+ ot->idname = "PARTICLE_OT_select_tips";
+ ot->description = "Select tips of all visible particles";
+
+ /* api callbacks */
+ ot->exec = select_tips_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_select_action(ot, SEL_SELECT);
+}
+
+/*********************** select random operator ************************/
+
+enum { RAN_HAIR, RAN_POINTS };
+
+static EnumPropertyItem select_random_type_items[] = {
+ {RAN_HAIR, "HAIR", 0, "Hair", ""},
+ {RAN_POINTS, "POINTS", 0, "Points", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static int select_random_exec(bContext *C, wmOperator *op)
+{
+ PEData data;
+ int type;
+ Scene *scene;
+ Object *ob;
+
+ /* used by LOOP_VISIBLE_POINTS, LOOP_VISIBLE_KEYS and LOOP_KEYS */
+ PTCacheEdit *edit;
+ PTCacheEditPoint *point;
+ PTCacheEditKey *key;
+ int p;
+ int k;
+
+ const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
+ const int seed = WM_operator_properties_select_random_seed_increment_get(op);
+ const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
+ RNG *rng;
+
+ type = RNA_enum_get(op->ptr, "type");
+
+ PE_set_data(C, &data);
+ data.select_action = SEL_SELECT;
+ scene = CTX_data_scene(C);
+ ob = CTX_data_active_object(C);
+ edit = PE_get_current(scene, ob);
+
+ rng = BLI_rng_new_srandom(seed);
+
+ switch (type) {
+ case RAN_HAIR:
+ LOOP_VISIBLE_POINTS {
+ int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT;
+ LOOP_KEYS {
+ select_action_apply (point, key, flag);
+ }
+ }
+ break;
+ case RAN_POINTS:
+ LOOP_VISIBLE_POINTS {
+ LOOP_VISIBLE_KEYS {
+ int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT;
+ select_action_apply (point, key, flag);
+ }
+ }
+ break;
+ }
+
+ BLI_rng_free(rng);
+
+ PE_update_selection(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_select_random(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Random";
+ ot->idname = "PARTICLE_OT_select_random";
+ ot->description = "Select a randomly distributed set of hair or points";
+
+ /* api callbacks */
+ ot->exec = select_random_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_select_random(ot);
+ ot->prop = RNA_def_enum (ot->srna, "type", select_random_type_items, RAN_HAIR,
+ "Type", "Select either hair or points");
+}
+
+/************************ select linked operator ************************/
+
+static int select_linked_exec(bContext *C, wmOperator *op)
+{
+ PEData data;
+ int mval[2];
+ int location[2];
+
+ RNA_int_get_array(op->ptr, "location", location);
+ mval[0] = location[0];
+ mval[1] = location[1];
+
+ PE_set_view3d_data(C, &data);
+ data.mval= mval;
+ data.rad=75.0f;
+ data.select= !RNA_boolean_get(op->ptr, "deselect");
+
+ for_mouse_hit_keys(&data, select_keys, 1); /* nearest only */
+ PE_update_selection(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ RNA_int_set_array(op->ptr, "location", event->mval);
+ return select_linked_exec(C, op);
+}
+
+void PARTICLE_OT_select_linked(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Linked";
+ ot->idname = "PARTICLE_OT_select_linked";
+ ot->description = "Select nearest particle from mouse pointer";
+
+ /* api callbacks */
+ ot->exec = select_linked_exec;
+ ot->invoke = select_linked_invoke;
+ ot->poll = PE_poll_view3d;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them");
+ RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
+}
+
+/************************ border select operator ************************/
+void PE_deselect_all_visible(PTCacheEdit *edit)
+{
+ POINT_P; KEY_K;
+
+ LOOP_VISIBLE_POINTS {
+ LOOP_SELECTED_KEYS {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+}
+
+int PE_border_select(bContext *C, rcti *rect, bool select, bool extend)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ PEData data;
+
+ if (!PE_start_edit(edit))
+ return OPERATOR_CANCELLED;
+
+ if (extend == 0 && select)
+ PE_deselect_all_visible(edit);
+
+ PE_set_view3d_data(C, &data);
+ data.rect= rect;
+ data.select= select;
+
+ for_mouse_hit_keys(&data, select_key, 0);
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/************************ circle select operator ************************/
+
+int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ PEData data;
+
+ if (!PE_start_edit(edit))
+ return OPERATOR_FINISHED;
+
+ PE_set_view3d_data(C, &data);
+ data.mval= mval;
+ data.rad= rad;
+ data.select= selecting;
+
+ for_mouse_hit_keys(&data, select_key, 0);
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/************************ lasso select operator ************************/
+
+int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, bool extend, bool select)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ ARegion *ar= CTX_wm_region(C);
+ ParticleEditSettings *pset= PE_settings(scene);
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ ParticleSystem *psys = edit->psys;
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ POINT_P; KEY_K;
+ float co[3], mat[4][4];
+ int screen_co[2];
+
+ PEData data;
+
+ unit_m4(mat);
+
+ if (!PE_start_edit(edit))
+ return OPERATOR_CANCELLED;
+
+ if (extend == 0 && select)
+ PE_deselect_all_visible(edit);
+
+ /* only for depths */
+ PE_set_view3d_data(C, &data);
+
+ LOOP_VISIBLE_POINTS {
+ if (edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR))
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
+
+ if (pset->selectmode==SCE_SELECT_POINT) {
+ LOOP_KEYS {
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ if ((ED_view3d_project_int_global(ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) == V3D_PROJ_RET_OK) &&
+ BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], IS_CLIPPED) &&
+ key_test_depth(&data, co, screen_co))
+ {
+ if (select) {
+ if (!(key->flag & PEK_SELECT)) {
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+ else {
+ if (key->flag & PEK_SELECT) {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+ }
+ }
+ }
+ else if (pset->selectmode==SCE_SELECT_END) {
+ if (point->totkey) {
+ key= point->keys + point->totkey - 1;
+
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ if ((ED_view3d_project_int_global(ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) == V3D_PROJ_RET_OK) &&
+ BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], IS_CLIPPED) &&
+ key_test_depth(&data, co, screen_co))
+ {
+ if (select) {
+ if (!(key->flag & PEK_SELECT)) {
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+ else {
+ if (key->flag & PEK_SELECT) {
+ key->flag &= ~PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/*************************** hide operator **************************/
+
+static int hide_exec(bContext *C, wmOperator *op)
+{
+ Object *ob= CTX_data_active_object(C);
+ Scene *scene= CTX_data_scene(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ POINT_P; KEY_K;
+
+ if (RNA_enum_get(op->ptr, "unselected")) {
+ LOOP_UNSELECTED_POINTS {
+ point->flag |= PEP_HIDE;
+ point->flag |= PEP_EDIT_RECALC;
+
+ LOOP_KEYS
+ key->flag &= ~PEK_SELECT;
+ }
+ }
+ else {
+ LOOP_SELECTED_POINTS {
+ point->flag |= PEP_HIDE;
+ point->flag |= PEP_EDIT_RECALC;
+
+ LOOP_KEYS
+ key->flag &= ~PEK_SELECT;
+ }
+ }
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_hide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hide Selected";
+ ot->idname = "PARTICLE_OT_hide";
+ ot->description = "Hide selected particles";
+
+ /* api callbacks */
+ ot->exec = hide_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
+}
+
+/*************************** reveal operator **************************/
+
+static int reveal_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob= CTX_data_active_object(C);
+ Scene *scene= CTX_data_scene(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ POINT_P; KEY_K;
+
+ LOOP_POINTS {
+ if (point->flag & PEP_HIDE) {
+ point->flag &= ~PEP_HIDE;
+ point->flag |= PEP_EDIT_RECALC;
+
+ LOOP_KEYS
+ key->flag |= PEK_SELECT;
+ }
+ }
+
+ PE_update_selection(scene, ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_reveal(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Reveal";
+ ot->idname = "PARTICLE_OT_reveal";
+ ot->description = "Show hidden particles";
+
+ /* api callbacks */
+ ot->exec = reveal_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ select less operator ************************/
+
+static void select_less_keys(PEData *data, int point_index)
+{
+ PTCacheEdit *edit= data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ KEY_K;
+
+ LOOP_SELECTED_KEYS {
+ if (k==0) {
+ if (((key+1)->flag&PEK_SELECT)==0)
+ key->flag |= PEK_TAG;
+ }
+ else if (k==point->totkey-1) {
+ if (((key-1)->flag&PEK_SELECT)==0)
+ key->flag |= PEK_TAG;
+ }
+ else {
+ if ((((key-1)->flag & (key+1)->flag) & PEK_SELECT)==0)
+ key->flag |= PEK_TAG;
+ }
+ }
+
+ LOOP_KEYS {
+ if (key->flag&PEK_TAG) {
+ key->flag &= ~(PEK_TAG|PEK_SELECT);
+ point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
+ }
+ }
+}
+
+static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PEData data;
+
+ PE_set_data(C, &data);
+ foreach_point(&data, select_less_keys);
+
+ PE_update_selection(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_select_less(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Less";
+ ot->idname = "PARTICLE_OT_select_less";
+ ot->description = "Deselect boundary selected keys of each particle";
+
+ /* api callbacks */
+ ot->exec = select_less_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ select more operator ************************/
+
+static void select_more_keys(PEData *data, int point_index)
+{
+ PTCacheEdit *edit= data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ KEY_K;
+
+ LOOP_KEYS {
+ if (key->flag & PEK_SELECT) continue;
+
+ if (k==0) {
+ if ((key+1)->flag&PEK_SELECT)
+ key->flag |= PEK_TAG;
+ }
+ else if (k==point->totkey-1) {
+ if ((key-1)->flag&PEK_SELECT)
+ key->flag |= PEK_TAG;
+ }
+ else {
+ if (((key-1)->flag | (key+1)->flag) & PEK_SELECT)
+ key->flag |= PEK_TAG;
+ }
+ }
+
+ LOOP_KEYS {
+ if (key->flag&PEK_TAG) {
+ key->flag &= ~PEK_TAG;
+ key->flag |= PEK_SELECT;
+ point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
+ }
+ }
+}
+
+static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PEData data;
+
+ PE_set_data(C, &data);
+ foreach_point(&data, select_more_keys);
+
+ PE_update_selection(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_select_more(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select More";
+ ot->idname = "PARTICLE_OT_select_more";
+ ot->description = "Select keys linked to boundary selected keys of each particle";
+
+ /* api callbacks */
+ ot->exec = select_more_exec;
+ ot->poll = PE_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ rekey operator ************************/
+
+static void rekey_particle(PEData *data, int pa_index)
+{
+ PTCacheEdit *edit= data->edit;
+ ParticleSystem *psys= edit->psys;
+ ParticleSimulationData sim= {0};
+ ParticleData *pa= psys->particles + pa_index;
+ PTCacheEditPoint *point = edit->points + pa_index;
+ ParticleKey state;
+ HairKey *key, *new_keys, *okey;
+ PTCacheEditKey *ekey;
+ float dval, sta, end;
+ int k;
+
+ sim.scene= data->scene;
+ sim.ob= data->ob;
+ sim.psys= edit->psys;
+
+ pa->flag |= PARS_REKEY;
+
+ key= new_keys= MEM_callocN(data->totrekey * sizeof(HairKey), "Hair re-key keys");
+
+ okey = pa->hair;
+ /* root and tip stay the same */
+ copy_v3_v3(key->co, okey->co);
+ copy_v3_v3((key + data->totrekey - 1)->co, (okey + pa->totkey - 1)->co);
+
+ sta= key->time= okey->time;
+ end= (key + data->totrekey - 1)->time= (okey + pa->totkey - 1)->time;
+ dval= (end - sta) / (float)(data->totrekey - 1);
+
+ /* interpolate new keys from old ones */
+ for (k=1, key++; k<data->totrekey-1; k++, key++) {
+ state.time= (float)k / (float)(data->totrekey-1);
+ psys_get_particle_on_path(&sim, pa_index, &state, 0);
+ copy_v3_v3(key->co, state.co);
+ key->time= sta + k * dval;
+ }
+
+ /* replace keys */
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ pa->hair= new_keys;
+
+ point->totkey=pa->totkey=data->totrekey;
+
+
+ if (point->keys)
+ MEM_freeN(point->keys);
+ ekey= point->keys= MEM_callocN(pa->totkey * sizeof(PTCacheEditKey), "Hair re-key edit keys");
+
+ for (k=0, key=pa->hair; k<pa->totkey; k++, key++, ekey++) {
+ ekey->co= key->co;
+ ekey->time= &key->time;
+ ekey->flag |= PEK_SELECT;
+ if (!(psys->flag & PSYS_GLOBAL_HAIR))
+ ekey->flag |= PEK_USE_WCO;
+ }
+
+ pa->flag &= ~PARS_REKEY;
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static int rekey_exec(bContext *C, wmOperator *op)
+{
+ PEData data;
+
+ PE_set_data(C, &data);
+
+ data.dval= 1.0f / (float)(data.totrekey-1);
+ data.totrekey= RNA_int_get(op->ptr, "keys_number");
+
+ foreach_selected_point(&data, rekey_particle);
+
+ recalc_lengths(data.edit);
+ PE_update_object(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_rekey(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Rekey";
+ ot->idname = "PARTICLE_OT_rekey";
+ ot->description = "Change the number of keys of selected particles (root and tip keys included)";
+
+ /* api callbacks */
+ ot->exec = rekey_exec;
+ ot->invoke = WM_operator_props_popup;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "keys_number", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
+}
+
+static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float path_time)
+{
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ ParticleSystem *psys;
+ ParticleSimulationData sim= {0};
+ ParticleData *pa;
+ ParticleKey state;
+ HairKey *new_keys, *key;
+ PTCacheEditKey *ekey;
+ int k;
+
+ if (!edit || !edit->psys) return;
+
+ psys = edit->psys;
+
+ sim.scene= scene;
+ sim.ob= ob;
+ sim.psys= psys;
+
+ pa= psys->particles + pa_index;
+
+ pa->flag |= PARS_REKEY;
+
+ key= new_keys= MEM_dupallocN(pa->hair);
+
+ /* interpolate new keys from old ones (roots stay the same) */
+ for (k=1, key++; k < pa->totkey; k++, key++) {
+ state.time= path_time * (float)k / (float)(pa->totkey-1);
+ psys_get_particle_on_path(&sim, pa_index, &state, 0);
+ copy_v3_v3(key->co, state.co);
+ }
+
+ /* replace hair keys */
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ pa->hair= new_keys;
+
+ /* update edit pointers */
+ for (k=0, key=pa->hair, ekey=edit->points[pa_index].keys; k<pa->totkey; k++, key++, ekey++) {
+ ekey->co= key->co;
+ ekey->time= &key->time;
+ }
+
+ pa->flag &= ~PARS_REKEY;
+}
+
+/************************* utilities **************************/
+
+static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror)
+{
+ PTCacheEdit *edit = psys->edit;
+ ParticleData *pa, *npa=0, *new_pars=0;
+ POINT_P;
+ PTCacheEditPoint *npoint=0, *new_points=0;
+ ParticleSystemModifierData *psmd;
+ int i, new_totpart= psys->totpart, removed= 0;
+
+ if (mirror) {
+ /* mirror tags */
+ psmd= psys_get_modifier(ob, psys);
+
+ LOOP_TAGGED_POINTS {
+ PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);
+ }
+ }
+
+ LOOP_TAGGED_POINTS {
+ new_totpart--;
+ removed++;
+ }
+
+ if (new_totpart != psys->totpart) {
+ if (new_totpart) {
+ npa= new_pars= MEM_callocN(new_totpart * sizeof(ParticleData), "ParticleData array");
+ npoint= new_points= MEM_callocN(new_totpart * sizeof(PTCacheEditPoint), "PTCacheEditKey array");
+
+ if (ELEM(NULL, new_pars, new_points)) {
+ /* allocation error! */
+ if (new_pars)
+ MEM_freeN(new_pars);
+ if (new_points)
+ MEM_freeN(new_points);
+ return 0;
+ }
+ }
+
+ pa= psys->particles;
+ point= edit->points;
+ for (i=0; i<psys->totpart; i++, pa++, point++) {
+ if (point->flag & PEP_TAG) {
+ if (point->keys)
+ MEM_freeN(point->keys);
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ }
+ else {
+ memcpy(npa, pa, sizeof(ParticleData));
+ memcpy(npoint, point, sizeof(PTCacheEditPoint));
+ npa++;
+ npoint++;
+ }
+ }
+
+ if (psys->particles) MEM_freeN(psys->particles);
+ psys->particles= new_pars;
+
+ if (edit->points) MEM_freeN(edit->points);
+ edit->points= new_points;
+
+ if (edit->mirror_cache) {
+ MEM_freeN(edit->mirror_cache);
+ edit->mirror_cache= NULL;
+ }
+
+ if (psys->child) {
+ MEM_freeN(psys->child);
+ psys->child= NULL;
+ psys->totchild=0;
+ }
+
+ edit->totpoint= psys->totpart= new_totpart;
+ }
+
+ return removed;
+}
+
+static void remove_tagged_keys(Object *ob, ParticleSystem *psys)
+{
+ PTCacheEdit *edit= psys->edit;
+ ParticleData *pa;
+ HairKey *hkey, *nhkey, *new_hkeys=0;
+ POINT_P; KEY_K;
+ PTCacheEditKey *nkey, *new_keys;
+ ParticleSystemModifierData *psmd;
+ short new_totkey;
+
+ if (pe_x_mirror(ob)) {
+ /* mirror key tags */
+ psmd= psys_get_modifier(ob, psys);
+
+ LOOP_POINTS {
+ LOOP_TAGGED_KEYS {
+ PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);
+ break;
+ }
+ }
+ }
+
+ LOOP_POINTS {
+ new_totkey= point->totkey;
+ LOOP_TAGGED_KEYS {
+ new_totkey--;
+ }
+ /* we can't have elements with less than two keys*/
+ if (new_totkey < 2)
+ point->flag |= PEP_TAG;
+ }
+ remove_tagged_particles(ob, psys, pe_x_mirror(ob));
+
+ LOOP_POINTS {
+ pa = psys->particles + p;
+ new_totkey= pa->totkey;
+
+ LOOP_TAGGED_KEYS {
+ new_totkey--;
+ }
+
+ if (new_totkey != pa->totkey) {
+ nhkey= new_hkeys= MEM_callocN(new_totkey*sizeof(HairKey), "HairKeys");
+ nkey= new_keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
+
+ hkey= pa->hair;
+ LOOP_KEYS {
+ while (key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
+ key++;
+ hkey++;
+ }
+
+ if (hkey < pa->hair + pa->totkey) {
+ copy_v3_v3(nhkey->co, hkey->co);
+ nhkey->editflag = hkey->editflag;
+ nhkey->time= hkey->time;
+ nhkey->weight= hkey->weight;
+
+ nkey->co= nhkey->co;
+ nkey->time= &nhkey->time;
+ /* these can be copied from old edit keys */
+ nkey->flag = key->flag;
+ nkey->ftime = key->ftime;
+ nkey->length = key->length;
+ copy_v3_v3(nkey->world_co, key->world_co);
+ }
+ nkey++;
+ nhkey++;
+ hkey++;
+ }
+
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+
+ if (point->keys)
+ MEM_freeN(point->keys);
+
+ pa->hair= new_hkeys;
+ point->keys= new_keys;
+
+ point->totkey= pa->totkey= new_totkey;
+
+ /* flag for recalculating length */
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+}
+
+/************************ subdivide opertor *********************/
+
+/* works like normal edit mode subdivide, inserts keys between neighboring selected keys */
+static void subdivide_particle(PEData *data, int pa_index)
+{
+ PTCacheEdit *edit= data->edit;
+ ParticleSystem *psys= edit->psys;
+ ParticleSimulationData sim= {0};
+ ParticleData *pa= psys->particles + pa_index;
+ PTCacheEditPoint *point = edit->points + pa_index;
+ ParticleKey state;
+ HairKey *key, *nkey, *new_keys;
+ PTCacheEditKey *ekey, *nekey, *new_ekeys;
+
+ int k;
+ short totnewkey=0;
+ float endtime;
+
+ sim.scene= data->scene;
+ sim.ob= data->ob;
+ sim.psys= edit->psys;
+
+ for (k=0, ekey=point->keys; k<pa->totkey-1; k++, ekey++) {
+ if (ekey->flag&PEK_SELECT && (ekey+1)->flag&PEK_SELECT)
+ totnewkey++;
+ }
+
+ if (totnewkey==0) return;
+
+ pa->flag |= PARS_REKEY;
+
+ nkey= new_keys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(HairKey)), "Hair subdivide keys");
+ nekey= new_ekeys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(PTCacheEditKey)), "Hair subdivide edit keys");
+
+ key = pa->hair;
+ endtime= key[pa->totkey-1].time;
+
+ for (k=0, ekey=point->keys; k<pa->totkey-1; k++, key++, ekey++) {
+
+ memcpy(nkey, key, sizeof(HairKey));
+ memcpy(nekey, ekey, sizeof(PTCacheEditKey));
+
+ nekey->co= nkey->co;
+ nekey->time= &nkey->time;
+
+ nkey++;
+ nekey++;
+
+ if (ekey->flag & PEK_SELECT && (ekey+1)->flag & PEK_SELECT) {
+ nkey->time = (key->time + (key + 1)->time) * 0.5f;
+ state.time = (endtime != 0.0f) ? nkey->time / endtime: 0.0f;
+ psys_get_particle_on_path(&sim, pa_index, &state, 0);
+ copy_v3_v3(nkey->co, state.co);
+
+ nekey->co= nkey->co;
+ nekey->time = &nkey->time;
+ nekey->flag |= PEK_SELECT;
+ if (!(psys->flag & PSYS_GLOBAL_HAIR))
+ nekey->flag |= PEK_USE_WCO;
+
+ nekey++;
+ nkey++;
+ }
+ }
+ /*tip still not copied*/
+ memcpy(nkey, key, sizeof(HairKey));
+ memcpy(nekey, ekey, sizeof(PTCacheEditKey));
+
+ nekey->co= nkey->co;
+ nekey->time= &nkey->time;
+
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ pa->hair= new_keys;
+
+ if (point->keys)
+ MEM_freeN(point->keys);
+ point->keys= new_ekeys;
+
+ point->totkey = pa->totkey = pa->totkey + totnewkey;
+ point->flag |= PEP_EDIT_RECALC;
+ pa->flag &= ~PARS_REKEY;
+}
+
+static int subdivide_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PEData data;
+
+ PE_set_data(C, &data);
+ foreach_point(&data, subdivide_particle);
+
+ recalc_lengths(data.edit);
+ PE_update_object(data.scene, data.ob, 1);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_subdivide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Subdivide";
+ ot->idname = "PARTICLE_OT_subdivide";
+ ot->description = "Subdivide selected particles segments (adds keys)";
+
+ /* api callbacks */
+ ot->exec = subdivide_exec;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ remove doubles opertor *********************/
+
+static int remove_doubles_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ ParticleSystem *psys = edit->psys;
+ ParticleSystemModifierData *psmd;
+ KDTree *tree;
+ KDTreeNearest nearest[10];
+ POINT_P;
+ float mat[4][4], co[3], threshold= RNA_float_get(op->ptr, "threshold");
+ int n, totn, removed, totremoved;
+
+ if (psys->flag & PSYS_GLOBAL_HAIR)
+ return OPERATOR_CANCELLED;
+
+ edit= psys->edit;
+ psmd= psys_get_modifier(ob, psys);
+ totremoved= 0;
+
+ do {
+ removed= 0;
+
+ tree=BLI_kdtree_new(psys->totpart);
+
+ /* insert particles into kd tree */
+ LOOP_SELECTED_POINTS {
+ psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles+p, mat);
+ copy_v3_v3(co, point->keys->co);
+ mul_m4_v3(mat, co);
+ BLI_kdtree_insert(tree, p, co);
+ }
+
+ BLI_kdtree_balance(tree);
+
+ /* tag particles to be removed */
+ LOOP_SELECTED_POINTS {
+ psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles+p, mat);
+ copy_v3_v3(co, point->keys->co);
+ mul_m4_v3(mat, co);
+
+ totn = BLI_kdtree_find_nearest_n(tree, co, nearest, 10);
+
+ for (n=0; n<totn; n++) {
+ /* this needs a custom threshold still */
+ if (nearest[n].index > p && nearest[n].dist < threshold) {
+ if (!(point->flag & PEP_TAG)) {
+ point->flag |= PEP_TAG;
+ removed++;
+ }
+ }
+ }
+ }
+
+ BLI_kdtree_free(tree);
+
+ /* remove tagged particles - don't do mirror here! */
+ remove_tagged_particles(ob, psys, 0);
+ totremoved += removed;
+ } while (removed);
+
+ if (totremoved == 0)
+ return OPERATOR_CANCELLED;
+
+ BKE_reportf(op->reports, RPT_INFO, "Removed %d double particles", totremoved);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Doubles";
+ ot->idname = "PARTICLE_OT_remove_doubles";
+ ot->description = "Remove selected particles close enough of others";
+
+ /* api callbacks */
+ ot->exec = remove_doubles_exec;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX,
+ "Merge Distance", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
+}
+
+
+static int weight_set_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ ParticleEditSettings *pset= PE_settings(scene);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ ParticleSystem *psys = edit->psys;
+ POINT_P;
+ KEY_K;
+ HairKey *hkey;
+ float weight;
+ ParticleBrushData *brush= &pset->brush[pset->brushtype];
+ float factor= RNA_float_get(op->ptr, "factor");
+
+ weight= brush->strength;
+ edit= psys->edit;
+
+ LOOP_SELECTED_POINTS {
+ ParticleData *pa= psys->particles + p;
+
+ LOOP_SELECTED_KEYS {
+ hkey= pa->hair + k;
+ hkey->weight= interpf(weight, hkey->weight, factor);
+ }
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_weight_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Weight Set";
+ ot->idname = "PARTICLE_OT_weight_set";
+ ot->description = "Set the weight of selected keys";
+
+ /* api callbacks */
+ ot->exec = weight_set_exec;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_float(ot->srna, "factor", 1, 0, 1, "Factor",
+ "Interpolation factor between current brush weight, and keys' weights", 0, 1);
+}
+
+/************************ cursor drawing *******************************/
+
+static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
+{
+ Scene *scene = CTX_data_scene(C);
+ ParticleEditSettings *pset= PE_settings(scene);
+ ParticleBrushData *brush;
+
+ if (pset->brushtype < 0)
+ return;
+
+ brush= &pset->brush[pset->brushtype];
+
+ if (brush) {
+ glPushMatrix();
+
+ glTranslatef((float)x, (float)y, 0.0f);
+
+ glColor4ub(255, 255, 255, 128);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ glutil_draw_lined_arc(0.0, M_PI*2.0, pe_brush_size_get(scene, brush), 40);
+ glDisable(GL_BLEND);
+ glDisable(GL_LINE_SMOOTH);
+
+ glPopMatrix();
+ }
+}
+
+static void toggle_particle_cursor(bContext *C, int enable)
+{
+ ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
+
+ if (pset->paintcursor && !enable) {
+ WM_paint_cursor_end(CTX_wm_manager(C), pset->paintcursor);
+ pset->paintcursor = NULL;
+ }
+ else if (enable)
+ pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_view3d, brush_drawcursor, NULL);
+}
+
+/*************************** delete operator **************************/
+
+enum { DEL_PARTICLE, DEL_KEY };
+
+static EnumPropertyItem delete_type_items[] = {
+ {DEL_PARTICLE, "PARTICLE", 0, "Particle", ""},
+ {DEL_KEY, "KEY", 0, "Key", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+static void set_delete_particle(PEData *data, int pa_index)
+{
+ PTCacheEdit *edit= data->edit;
+
+ edit->points[pa_index].flag |= PEP_TAG;
+}
+
+static void set_delete_particle_key(PEData *data, int pa_index, int key_index)
+{
+ PTCacheEdit *edit= data->edit;
+
+ edit->points[pa_index].keys[key_index].flag |= PEK_TAG;
+}
+
+static int delete_exec(bContext *C, wmOperator *op)
+{
+ PEData data;
+ int type= RNA_enum_get(op->ptr, "type");
+
+ PE_set_data(C, &data);
+
+ if (type == DEL_KEY) {
+ foreach_selected_key(&data, set_delete_particle_key);
+ remove_tagged_keys(data.ob, data.edit->psys);
+ recalc_lengths(data.edit);
+ }
+ else if (type == DEL_PARTICLE) {
+ foreach_selected_point(&data, set_delete_particle);
+ remove_tagged_particles(data.ob, data.edit->psys, pe_x_mirror(data.ob));
+ recalc_lengths(data.edit);
+ }
+
+ DAG_id_tag_update(&data.ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_delete(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Delete";
+ ot->idname = "PARTICLE_OT_delete";
+ ot->description = "Delete selected particles or keys";
+
+ /* api callbacks */
+ ot->exec = delete_exec;
+ ot->invoke = WM_menu_invoke;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys");
+}
+
+/*************************** mirror operator **************************/
+
+static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
+{
+ Mesh *me= (Mesh *)(ob->data);
+ ParticleSystemModifierData *psmd;
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ ParticleSystem *psys = edit->psys;
+ ParticleData *pa, *newpa, *new_pars;
+ PTCacheEditPoint *newpoint, *new_points;
+ POINT_P; KEY_K;
+ HairKey *hkey;
+ int *mirrorfaces = NULL;
+ int rotation, totpart, newtotpart;
+
+ if (psys->flag & PSYS_GLOBAL_HAIR)
+ return;
+
+ psmd= psys_get_modifier(ob, psys);
+ if (!psmd->dm_final)
+ return;
+
+ const bool use_dm_final_indices = (psys->part->use_modifier_stack && !psmd->dm_final->deformedOnly);
+
+ /* NOTE: this is not nice to use tessfaces but hard to avoid since pa->num uses tessfaces */
+ BKE_mesh_tessface_ensure(me);
+
+ /* Note: In case psys uses DM tessface indices, we mirror final DM itself, not orig mesh. Avoids an (impossible)
+ * dm -> orig -> dm tessface indices conversion... */
+ mirrorfaces = mesh_get_x_mirror_faces(ob, NULL, use_dm_final_indices ? psmd->dm_final : NULL);
+
+ if (!edit->mirror_cache)
+ PE_update_mirror_cache(ob, psys);
+
+ totpart= psys->totpart;
+ newtotpart= psys->totpart;
+ LOOP_VISIBLE_POINTS {
+ pa = psys->particles + p;
+
+ if (!tagged) {
+ if (point_is_selected(point)) {
+ if (edit->mirror_cache[p] != -1) {
+ /* already has a mirror, don't need to duplicate */
+ PE_mirror_particle(ob, psmd->dm_final, psys, pa, NULL);
+ continue;
+ }
+ else
+ point->flag |= PEP_TAG;
+ }
+ }
+
+ if ((point->flag & PEP_TAG) && mirrorfaces[pa->num*2] != -1)
+ newtotpart++;
+ }
+
+ if (newtotpart != psys->totpart) {
+ MFace *mtessface = use_dm_final_indices ? psmd->dm_final->getTessFaceArray(psmd->dm_final) : me->mface;
+
+ /* allocate new arrays and copy existing */
+ new_pars= MEM_callocN(newtotpart*sizeof(ParticleData), "ParticleData new");
+ new_points= MEM_callocN(newtotpart*sizeof(PTCacheEditPoint), "PTCacheEditPoint new");
+
+ if (psys->particles) {
+ memcpy(new_pars, psys->particles, totpart*sizeof(ParticleData));
+ MEM_freeN(psys->particles);
+ }
+ psys->particles= new_pars;
+
+ if (edit->points) {
+ memcpy(new_points, edit->points, totpart*sizeof(PTCacheEditPoint));
+ MEM_freeN(edit->points);
+ }
+ edit->points= new_points;
+
+ if (edit->mirror_cache) {
+ MEM_freeN(edit->mirror_cache);
+ edit->mirror_cache= NULL;
+ }
+
+ edit->totpoint= psys->totpart= newtotpart;
+
+ /* create new elements */
+ newpa= psys->particles + totpart;
+ newpoint= edit->points + totpart;
+
+ for (p=0, point=edit->points; p<totpart; p++, point++) {
+ pa = psys->particles + p;
+ const int pa_num = pa->num;
+
+ if (point->flag & PEP_HIDE)
+ continue;
+
+ if (!(point->flag & PEP_TAG) || mirrorfaces[pa_num * 2] == -1)
+ continue;
+
+ /* duplicate */
+ *newpa= *pa;
+ *newpoint= *point;
+ if (pa->hair) newpa->hair= MEM_dupallocN(pa->hair);
+ if (point->keys) newpoint->keys= MEM_dupallocN(point->keys);
+
+ /* rotate weights according to vertex index rotation */
+ rotation= mirrorfaces[pa_num * 2 + 1];
+ newpa->fuv[0] = pa->fuv[2];
+ newpa->fuv[1] = pa->fuv[1];
+ newpa->fuv[2] = pa->fuv[0];
+ newpa->fuv[3] = pa->fuv[3];
+ while (rotation--) {
+ if (mtessface[pa_num].v4) {
+ SHIFT4(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2], newpa->fuv[3]);
+ }
+ else {
+ SHIFT3(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2]);
+ }
+ }
+
+ /* assign face index */
+ /* NOTE: mesh_get_x_mirror_faces generates -1 for non-found mirror, same as DMCACHE_NOTFOUND... */
+ newpa->num = mirrorfaces[pa_num * 2];
+
+ if (use_dm_final_indices) {
+ newpa->num_dmcache = DMCACHE_ISCHILD;
+ }
+ else {
+ newpa->num_dmcache = psys_particle_dm_face_lookup(
+ psmd->dm_final, psmd->dm_deformed, newpa->num, newpa->fuv, NULL);
+ }
+
+ /* update edit key pointers */
+ key= newpoint->keys;
+ for (k=0, hkey=newpa->hair; k<newpa->totkey; k++, hkey++, key++) {
+ key->co= hkey->co;
+ key->time= &hkey->time;
+ }
+
+ /* map key positions as mirror over x axis */
+ PE_mirror_particle(ob, psmd->dm_final, psys, pa, newpa);
+
+ newpa++;
+ newpoint++;
+ }
+ }
+
+ LOOP_POINTS {
+ point->flag &= ~PEP_TAG;
+ }
+
+ MEM_freeN(mirrorfaces);
+}
+
+static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+
+ PE_mirror_x(scene, ob, 0);
+
+ update_world_cos(ob, edit);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_mirror(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Mirror";
+ ot->idname = "PARTICLE_OT_mirror";
+ ot->description = "Duplicate and mirror the selected particles along the local X axis";
+
+ /* api callbacks */
+ ot->exec = mirror_exec;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************* brush edit callbacks ********************/
+
+static void brush_comb(PEData *data, float UNUSED(mat[4][4]), float imat[4][4], int point_index, int key_index, PTCacheEditKey *key)
+{
+ ParticleEditSettings *pset= PE_settings(data->scene);
+ float cvec[3], fac;
+
+ if (pset->flag & PE_LOCK_FIRST && key_index == 0) return;
+
+ fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->combfac);
+
+ copy_v3_v3(cvec, data->dvec);
+ mul_mat3_m4_v3(imat, cvec);
+ mul_v3_fl(cvec, fac);
+ add_v3_v3(key->co, cvec);
+
+ (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
+}
+
+static void brush_cut(PEData *data, int pa_index)
+{
+ PTCacheEdit *edit = data->edit;
+ ARegion *ar= data->vc.ar;
+ Object *ob= data->ob;
+ ParticleEditSettings *pset= PE_settings(data->scene);
+ ParticleCacheKey *key= edit->pathcache[pa_index];
+ float rad2, cut_time= 1.0;
+ float x0, x1, v0, v1, o0, o1, xo0, xo1, d, dv;
+ int k, cut, keys= (int)pow(2.0, (double)pset->draw_step);
+ int screen_co[2];
+
+ /* blunt scissors */
+ if (BLI_frand() > data->cutfac) return;
+
+ /* don't cut hidden */
+ if (edit->points[pa_index].flag & PEP_HIDE)
+ return;
+
+ if (ED_view3d_project_int_global(ar, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
+ return;
+
+ rad2= data->rad * data->rad;
+
+ cut=0;
+
+ x0 = (float)screen_co[0];
+ x1 = (float)screen_co[1];
+
+ o0= (float)data->mval[0];
+ o1= (float)data->mval[1];
+
+ xo0= x0 - o0;
+ xo1= x1 - o1;
+
+ /* check if root is inside circle */
+ if (xo0*xo0 + xo1*xo1 < rad2 && key_test_depth(data, key->co, screen_co)) {
+ cut_time= -1.0f;
+ cut= 1;
+ }
+ else {
+ /* calculate path time closest to root that was inside the circle */
+ for (k=1, key++; k<=keys; k++, key++) {
+
+ if ((ED_view3d_project_int_global(ar, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK) ||
+ key_test_depth(data, key->co, screen_co) == 0)
+ {
+ x0 = (float)screen_co[0];
+ x1 = (float)screen_co[1];
+
+ xo0= x0 - o0;
+ xo1= x1 - o1;
+ continue;
+ }
+
+ v0 = (float)screen_co[0] - x0;
+ v1 = (float)screen_co[1] - x1;
+
+ dv= v0*v0 + v1*v1;
+
+ d= (v0*xo1 - v1*xo0);
+
+ d= dv * rad2 - d*d;
+
+ if (d > 0.0f) {
+ d= sqrtf(d);
+
+ cut_time= -(v0*xo0 + v1*xo1 + d);
+
+ if (cut_time > 0.0f) {
+ cut_time /= dv;
+
+ if (cut_time < 1.0f) {
+ cut_time += (float)(k-1);
+ cut_time /= (float)keys;
+ cut= 1;
+ break;
+ }
+ }
+ }
+
+ x0 = (float)screen_co[0];
+ x1 = (float)screen_co[1];
+
+ xo0= x0 - o0;
+ xo1= x1 - o1;
+ }
+ }
+
+ if (cut) {
+ if (cut_time < 0.0f) {
+ edit->points[pa_index].flag |= PEP_TAG;
+ }
+ else {
+ rekey_particle_to_time(data->scene, ob, pa_index, cut_time);
+ edit->points[pa_index].flag |= PEP_EDIT_RECALC;
+ }
+ }
+}
+
+static void brush_length(PEData *data, int point_index)
+{
+ PTCacheEdit *edit= data->edit;
+ PTCacheEditPoint *point = edit->points + point_index;
+ KEY_K;
+ float dvec[3], pvec[3] = {0.0f, 0.0f, 0.0f};
+
+ LOOP_KEYS {
+ if (k==0) {
+ copy_v3_v3(pvec, key->co);
+ }
+ else {
+ sub_v3_v3v3(dvec, key->co, pvec);
+ copy_v3_v3(pvec, key->co);
+ mul_v3_fl(dvec, data->growfac);
+ add_v3_v3v3(key->co, (key-1)->co, dvec);
+ }
+ }
+
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static void brush_puff(PEData *data, int point_index)
+{
+ PTCacheEdit *edit = data->edit;
+ ParticleSystem *psys = edit->psys;
+ PTCacheEditPoint *point = edit->points + point_index;
+ KEY_K;
+ float mat[4][4], imat[4][4];
+
+ float onor_prev[3]; /* previous normal (particle-space) */
+ float ofs_prev[3]; /* accumulate offset for puff_volume (particle-space) */
+ float co_root[3], no_root[3]; /* root location and normal (global-space) */
+ float co_prev[3], co[3]; /* track key coords as we loop (global-space) */
+ float fac = 0.0f, length_accum = 0.0f;
+ bool puff_volume = false;
+ bool changed = false;
+
+ zero_v3(ofs_prev);
+
+ {
+ ParticleEditSettings *pset= PE_settings(data->scene);
+ ParticleBrushData *brush= &pset->brush[pset->brushtype];
+ puff_volume = (brush->flag & PE_BRUSH_DATA_PUFF_VOLUME) != 0;
+ }
+
+ if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
+ psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
+ invert_m4_m4(imat, mat);
+ }
+ else {
+ unit_m4(mat);
+ unit_m4(imat);
+ }
+
+ LOOP_KEYS {
+ float kco[3];
+
+ if (k==0) {
+ /* find root coordinate and normal on emitter */
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ mul_v3_m4v3(kco, data->ob->imat, co); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
+
+ point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL);
+ if (point_index == -1) return;
+
+ copy_v3_v3(co_root, co);
+ copy_v3_v3(no_root, &edit->emitter_cosnos[point_index * 6 + 3]);
+ mul_mat3_m4_v3(data->ob->obmat, no_root); /* normal into global-space */
+ normalize_v3(no_root);
+
+ if (puff_volume) {
+ copy_v3_v3(onor_prev, no_root);
+ mul_mat3_m4_v3(imat, onor_prev); /* global-space into particle space */
+ normalize_v3(onor_prev);
+ }
+
+ fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->pufffac);
+ fac *= 0.025f;
+ if (data->invert)
+ fac= -fac;
+ }
+ else {
+ /* compute position as if hair was standing up straight.
+ * */
+ float length;
+ copy_v3_v3(co_prev, co);
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ length = len_v3v3(co_prev, co);
+ length_accum += length;
+
+ if ((data->select==0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
+ float dco[3]; /* delta temp var */
+
+ madd_v3_v3v3fl(kco, co_root, no_root, length_accum);
+
+ /* blend between the current and straight position */
+ sub_v3_v3v3(dco, kco, co);
+ madd_v3_v3fl(co, dco, fac);
+ /* keep the same distance from the root or we get glitches [#35406] */
+ dist_ensure_v3_v3fl(co, co_root, length_accum);
+
+ /* re-use dco to compare before and after translation and add to the offset */
+ copy_v3_v3(dco, key->co);
+
+ mul_v3_m4v3(key->co, imat, co);
+
+ if (puff_volume) {
+ /* accumulate the total distance moved to apply to unselected
+ * keys that come after */
+ sub_v3_v3v3(ofs_prev, key->co, dco);
+ }
+ changed = true;
+ }
+ else {
+
+ if (puff_volume) {
+#if 0
+ /* this is simple but looks bad, adds annoying kinks */
+ add_v3_v3(key->co, ofs);
+#else
+ /* translate (not rotate) the rest of the hair if its not selected */
+ {
+#if 0 /* kindof works but looks worse then whats below */
+
+ /* Move the unselected point on a vector based on the
+ * hair direction and the offset */
+ float c1[3], c2[3];
+ sub_v3_v3v3(dco, lastco, co);
+ mul_mat3_m4_v3(imat, dco); /* into particle space */
+
+ /* move the point along a vector perpendicular to the
+ * hairs direction, reduces odd kinks, */
+ cross_v3_v3v3(c1, ofs, dco);
+ cross_v3_v3v3(c2, c1, dco);
+ normalize_v3(c2);
+ mul_v3_fl(c2, len_v3(ofs));
+ add_v3_v3(key->co, c2);
+#else
+ /* Move the unselected point on a vector based on the
+ * the normal of the closest geometry */
+ float oco[3], onor[3];
+ copy_v3_v3(oco, key->co);
+ mul_m4_v3(mat, oco);
+ mul_v3_m4v3(kco, data->ob->imat, oco); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
+
+ point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL);
+ if (point_index != -1) {
+ copy_v3_v3(onor, &edit->emitter_cosnos[point_index*6+3]);
+ mul_mat3_m4_v3(data->ob->obmat, onor); /* normal into worldspace */
+ mul_mat3_m4_v3(imat, onor); /* worldspace into particle space */
+ normalize_v3(onor);
+ }
+ else {
+ copy_v3_v3(onor, onor_prev);
+ }
+
+ if (!is_zero_v3(ofs_prev)) {
+ mul_v3_fl(onor, len_v3(ofs_prev));
+
+ add_v3_v3(key->co, onor);
+ }
+
+ copy_v3_v3(onor_prev, onor);
+#endif
+ }
+#endif
+ }
+ }
+ }
+ }
+
+ if (changed)
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+
+static void BKE_brush_weight_get(PEData *data, float UNUSED(mat[4][4]), float UNUSED(imat[4][4]), int point_index, int key_index, PTCacheEditKey *UNUSED(key))
+{
+ /* roots have full weight always */
+ if (key_index) {
+ PTCacheEdit *edit = data->edit;
+ ParticleSystem *psys = edit->psys;
+
+ ParticleData *pa= psys->particles + point_index;
+ pa->hair[key_index].weight = data->weightfac;
+
+ (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
+ }
+}
+
+static void brush_smooth_get(PEData *data, float mat[4][4], float UNUSED(imat[4][4]), int UNUSED(point_index), int key_index, PTCacheEditKey *key)
+{
+ if (key_index) {
+ float dvec[3];
+
+ sub_v3_v3v3(dvec, key->co, (key-1)->co);
+ mul_mat3_m4_v3(mat, dvec);
+ add_v3_v3(data->vec, dvec);
+ data->tot++;
+ }
+}
+
+static void brush_smooth_do(PEData *data, float UNUSED(mat[4][4]), float imat[4][4], int point_index, int key_index, PTCacheEditKey *key)
+{
+ float vec[3], dvec[3];
+
+ if (key_index) {
+ copy_v3_v3(vec, data->vec);
+ mul_mat3_m4_v3(imat, vec);
+
+ sub_v3_v3v3(dvec, key->co, (key-1)->co);
+
+ sub_v3_v3v3(dvec, vec, dvec);
+ mul_v3_fl(dvec, data->smoothfac);
+
+ add_v3_v3(key->co, dvec);
+ }
+
+ (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
+}
+
+/* convert from triangle barycentric weights to quad mean value weights */
+static void intersect_dm_quad_weights(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float w[4])
+{
+ float co[3], vert[4][3];
+
+ copy_v3_v3(vert[0], v1);
+ copy_v3_v3(vert[1], v2);
+ copy_v3_v3(vert[2], v3);
+ copy_v3_v3(vert[3], v4);
+
+ co[0] = v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2] + v4[0]*w[3];
+ co[1] = v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2] + v4[1]*w[3];
+ co[2] = v1[2]*w[0] + v2[2]*w[1] + v3[2]*w[2] + v4[2]*w[3];
+
+ interp_weights_poly_v3(w, vert, 4, co);
+}
+
+/* check intersection with a derivedmesh */
+static int particle_intersect_dm(Scene *scene, Object *ob, DerivedMesh *dm,
+ float *vert_cos,
+ const float co1[3], const float co2[3],
+ float *min_d, int *min_face, float *min_w,
+ float *face_minmax, float *pa_minmax,
+ float radius, float *ipoint)
+{
+ MFace *mface= NULL;
+ MVert *mvert= NULL;
+ int i, totface, intersect=0;
+ float cur_d, cur_uv[2], v1[3], v2[3], v3[3], v4[3], min[3], max[3], p_min[3], p_max[3];
+ float cur_ipoint[3];
+
+ if (dm == NULL) {
+ psys_disable_all(ob);
+
+ dm=mesh_get_derived_final(scene, ob, 0);
+ if (dm == NULL)
+ dm=mesh_get_derived_deform(scene, ob, 0);
+
+ psys_enable_all(ob);
+
+ if (dm == NULL)
+ return 0;
+ }
+
+ /* BMESH_ONLY, deform dm may not have tessface */
+ DM_ensure_tessface(dm);
+
+
+ if (pa_minmax==0) {
+ INIT_MINMAX(p_min, p_max);
+ minmax_v3v3_v3(p_min, p_max, co1);
+ minmax_v3v3_v3(p_min, p_max, co2);
+ }
+ else {
+ copy_v3_v3(p_min, pa_minmax);
+ copy_v3_v3(p_max, pa_minmax+3);
+ }
+
+ totface=dm->getNumTessFaces(dm);
+ mface=dm->getTessFaceDataArray(dm, CD_MFACE);
+ mvert=dm->getVertDataArray(dm, CD_MVERT);
+
+ /* lets intersect the faces */
+ for (i=0; i<totface; i++, mface++) {
+ if (vert_cos) {
+ copy_v3_v3(v1, vert_cos+3*mface->v1);
+ copy_v3_v3(v2, vert_cos+3*mface->v2);
+ copy_v3_v3(v3, vert_cos+3*mface->v3);
+ if (mface->v4)
+ copy_v3_v3(v4, vert_cos+3*mface->v4);
+ }
+ else {
+ copy_v3_v3(v1, mvert[mface->v1].co);
+ copy_v3_v3(v2, mvert[mface->v2].co);
+ copy_v3_v3(v3, mvert[mface->v3].co);
+ if (mface->v4)
+ copy_v3_v3(v4, mvert[mface->v4].co);
+ }
+
+ if (face_minmax==0) {
+ INIT_MINMAX(min, max);
+ DO_MINMAX(v1, min, max);
+ DO_MINMAX(v2, min, max);
+ DO_MINMAX(v3, min, max);
+ if (mface->v4)
+ DO_MINMAX(v4, min, max);
+ if (isect_aabb_aabb_v3(min, max, p_min, p_max)==0)
+ continue;
+ }
+ else {
+ copy_v3_v3(min, face_minmax+6*i);
+ copy_v3_v3(max, face_minmax+6*i+3);
+ if (isect_aabb_aabb_v3(min, max, p_min, p_max)==0)
+ continue;
+ }
+
+ if (radius>0.0f) {
+ if (isect_sweeping_sphere_tri_v3(co1, co2, radius, v2, v3, v1, &cur_d, cur_ipoint)) {
+ if (cur_d<*min_d) {
+ *min_d=cur_d;
+ copy_v3_v3(ipoint, cur_ipoint);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ if (mface->v4) {
+ if (isect_sweeping_sphere_tri_v3(co1, co2, radius, v4, v1, v3, &cur_d, cur_ipoint)) {
+ if (cur_d<*min_d) {
+ *min_d=cur_d;
+ copy_v3_v3(ipoint, cur_ipoint);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ }
+ }
+ else {
+ if (isect_line_segment_tri_v3(co1, co2, v1, v2, v3, &cur_d, cur_uv)) {
+ if (cur_d<*min_d) {
+ *min_d=cur_d;
+ min_w[0] = 1.0f - cur_uv[0] - cur_uv[1];
+ min_w[1] = cur_uv[0];
+ min_w[2] = cur_uv[1];
+ min_w[3] = 0.0f;
+ if (mface->v4)
+ intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ if (mface->v4) {
+ if (isect_line_segment_tri_v3(co1, co2, v1, v3, v4, &cur_d, cur_uv)) {
+ if (cur_d<*min_d) {
+ *min_d=cur_d;
+ min_w[0] = 1.0f - cur_uv[0] - cur_uv[1];
+ min_w[1] = 0.0f;
+ min_w[2] = cur_uv[0];
+ min_w[3] = cur_uv[1];
+ intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
+ *min_face=i;
+ intersect=1;
+ }
+ }
+ }
+ }
+ }
+ return intersect;
+}
+
+static int brush_add(PEData *data, short number)
+{
+ Scene *scene= data->scene;
+ Object *ob= data->ob;
+ DerivedMesh *dm;
+ PTCacheEdit *edit = data->edit;
+ ParticleSystem *psys= edit->psys;
+ ParticleData *add_pars;
+ ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys);
+ ParticleSimulationData sim= {0};
+ ParticleEditSettings *pset= PE_settings(scene);
+ int i, k, n= 0, totpart= psys->totpart;
+ float mco[2];
+ float dmx, dmy;
+ float co1[3], co2[3], min_d, imat[4][4];
+ float framestep, timestep;
+ short size= pset->brush[PE_BRUSH_ADD].size;
+ short size2= size*size;
+ RNG *rng;
+
+ invert_m4_m4(imat, ob->obmat);
+
+ if (psys->flag & PSYS_GLOBAL_HAIR)
+ return 0;
+
+ add_pars = MEM_callocN(number * sizeof(ParticleData), "ParticleData add");
+
+ rng = BLI_rng_new_srandom(psys->seed+data->mval[0]+data->mval[1]);
+
+ sim.scene= scene;
+ sim.ob= ob;
+ sim.psys= psys;
+ sim.psmd= psmd;
+
+ timestep= psys_get_timestep(&sim);
+
+ if (psys->part->use_modifier_stack || psmd->dm_final->deformedOnly) {
+ dm = psmd->dm_final;
+ }
+ else {
+ dm = psmd->dm_deformed;
+ }
+ BLI_assert(dm);
+
+ for (i=0; i<number; i++) {
+ if (number>1) {
+ dmx = size;
+ dmy = size;
+
+ /* rejection sampling to get points in circle */
+ while (dmx*dmx + dmy*dmy > size2) {
+ dmx= (2.0f*BLI_rng_get_float(rng) - 1.0f)*size;
+ dmy= (2.0f*BLI_rng_get_float(rng) - 1.0f)*size;
+ }
+ }
+ else {
+ dmx = 0.0f;
+ dmy = 0.0f;
+ }
+
+ mco[0] = data->mval[0] + dmx;
+ mco[1] = data->mval[1] + dmy;
+ ED_view3d_win_to_segment(data->vc.ar, data->vc.v3d, mco, co1, co2, true);
+
+ mul_m4_v3(imat, co1);
+ mul_m4_v3(imat, co2);
+ min_d=2.0;
+
+ /* warning, returns the derived mesh face */
+ if (particle_intersect_dm(scene, ob, dm, 0, co1, co2, &min_d, &add_pars[n].num_dmcache, add_pars[n].fuv, 0, 0, 0, 0)) {
+ if (psys->part->use_modifier_stack && !psmd->dm_final->deformedOnly) {
+ add_pars[n].num = add_pars[n].num_dmcache;
+ add_pars[n].num_dmcache = DMCACHE_ISCHILD;
+ }
+ else if (dm == psmd->dm_deformed) {
+ /* Final DM is not same topology as orig mesh, we have to map num_dmcache to real final dm. */
+ add_pars[n].num = add_pars[n].num_dmcache;
+ add_pars[n].num_dmcache = psys_particle_dm_face_lookup(
+ psmd->dm_final, psmd->dm_deformed,
+ add_pars[n].num, add_pars[n].fuv, NULL);
+ }
+ else {
+ add_pars[n].num = add_pars[n].num_dmcache;
+ }
+
+ if (add_pars[n].num != DMCACHE_NOTFOUND) {
+ n++;
+ }
+ }
+ }
+ if (n) {
+ int newtotpart=totpart+n;
+ float hairmat[4][4], cur_co[3];
+ KDTree *tree=0;
+ ParticleData *pa, *new_pars = MEM_callocN(newtotpart*sizeof(ParticleData), "ParticleData new");
+ PTCacheEditPoint *point, *new_points = MEM_callocN(newtotpart*sizeof(PTCacheEditPoint), "PTCacheEditPoint array new");
+ PTCacheEditKey *key;
+ HairKey *hkey;
+
+ /* save existing elements */
+ memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
+ memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
+
+ /* change old arrays to new ones */
+ if (psys->particles) MEM_freeN(psys->particles);
+ psys->particles= new_pars;
+
+ if (edit->points) MEM_freeN(edit->points);
+ edit->points= new_points;
+
+ if (edit->mirror_cache) {
+ MEM_freeN(edit->mirror_cache);
+ edit->mirror_cache= NULL;
+ }
+
+ /* create tree for interpolation */
+ if (pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) {
+ tree=BLI_kdtree_new(psys->totpart);
+
+ for (i=0, pa=psys->particles; i<totpart; i++, pa++) {
+ psys_particle_on_dm(psmd->dm_final, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, cur_co, 0, 0, 0, 0, 0);
+ BLI_kdtree_insert(tree, i, cur_co);
+ }
+
+ BLI_kdtree_balance(tree);
+ }
+
+ edit->totpoint= psys->totpart= newtotpart;
+
+ /* create new elements */
+ pa = psys->particles + totpart;
+ point = edit->points + totpart;
+
+ for (i=totpart; i<newtotpart; i++, pa++, point++) {
+ memcpy(pa, add_pars + i - totpart, sizeof(ParticleData));
+ pa->hair= MEM_callocN(pset->totaddkey * sizeof(HairKey), "BakeKey key add");
+ key= point->keys= MEM_callocN(pset->totaddkey * sizeof(PTCacheEditKey), "PTCacheEditKey add");
+ point->totkey= pa->totkey= pset->totaddkey;
+
+ for (k=0, hkey=pa->hair; k<pa->totkey; k++, hkey++, key++) {
+ key->co= hkey->co;
+ key->time= &hkey->time;
+
+ if (!(psys->flag & PSYS_GLOBAL_HAIR))
+ key->flag |= PEK_USE_WCO;
+ }
+
+ pa->size= 1.0f;
+ initialize_particle(&sim, pa);
+ reset_particle(&sim, pa, 0.0, 1.0);
+ point->flag |= PEP_EDIT_RECALC;
+ if (pe_x_mirror(ob))
+ point->flag |= PEP_TAG; /* signal for duplicate */
+
+ framestep= pa->lifetime/(float)(pset->totaddkey-1);
+
+ if (tree) {
+ ParticleData *ppa;
+ HairKey *thkey;
+ ParticleKey key3[3];
+ KDTreeNearest ptn[3];
+ int w, maxw;
+ float maxd, totw=0.0, weight[3];
+
+ psys_particle_on_dm(psmd->dm_final, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co1, 0, 0, 0, 0, 0);
+ maxw = BLI_kdtree_find_nearest_n(tree, co1, ptn, 3);
+
+ maxd= ptn[maxw-1].dist;
+
+ for (w=0; w<maxw; w++) {
+ weight[w] = (float)pow(2.0, (double)(-6.0f * ptn[w].dist / maxd));
+ totw += weight[w];
+ }
+ for (;w<3; w++) {
+ weight[w] = 0.0f;
+ }
+
+ if (totw > 0.0f) {
+ for (w=0; w<maxw; w++)
+ weight[w] /= totw;
+ }
+ else {
+ for (w=0; w<maxw; w++)
+ weight[w] = 1.0f/maxw;
+ }
+
+ ppa= psys->particles+ptn[0].index;
+
+ for (k=0; k<pset->totaddkey; k++) {
+ thkey= (HairKey *)pa->hair + k;
+ thkey->time= pa->time + k * framestep;
+
+ key3[0].time= thkey->time/ 100.0f;
+ psys_get_particle_on_path(&sim, ptn[0].index, key3, 0);
+ mul_v3_fl(key3[0].co, weight[0]);
+
+ /* TODO: interpolating the weight would be nicer */
+ thkey->weight= (ppa->hair+MIN2(k, ppa->totkey-1))->weight;
+
+ if (maxw>1) {
+ key3[1].time= key3[0].time;
+ psys_get_particle_on_path(&sim, ptn[1].index, &key3[1], 0);
+ mul_v3_fl(key3[1].co, weight[1]);
+ add_v3_v3(key3[0].co, key3[1].co);
+
+ if (maxw>2) {
+ key3[2].time= key3[0].time;
+ psys_get_particle_on_path(&sim, ptn[2].index, &key3[2], 0);
+ mul_v3_fl(key3[2].co, weight[2]);
+ add_v3_v3(key3[0].co, key3[2].co);
+ }
+ }
+
+ if (k==0)
+ sub_v3_v3v3(co1, pa->state.co, key3[0].co);
+
+ add_v3_v3v3(thkey->co, key3[0].co, co1);
+
+ thkey->time= key3[0].time;
+ }
+ }
+ else {
+ for (k=0, hkey=pa->hair; k<pset->totaddkey; k++, hkey++) {
+ madd_v3_v3v3fl(hkey->co, pa->state.co, pa->state.vel, k * framestep * timestep);
+ hkey->time += k * framestep;
+ hkey->weight = 1.f - (float)k/(float)(pset->totaddkey-1);
+ }
+ }
+ for (k=0, hkey=pa->hair; k<pset->totaddkey; k++, hkey++) {
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, pa, hairmat);
+ invert_m4_m4(imat, hairmat);
+ mul_m4_v3(imat, hkey->co);
+ }
+ }
+
+ if (tree)
+ BLI_kdtree_free(tree);
+ }
+
+ MEM_freeN(add_pars);
+
+ BLI_rng_free(rng);
+
+ return n;
+}
+
+/************************* brush edit operator ********************/
+
+typedef struct BrushEdit {
+ Scene *scene;
+ Object *ob;
+ PTCacheEdit *edit;
+
+ int first;
+ int lastmouse[2];
+ float zfac;
+
+ /* optional cached view settings to avoid setting on every mousemove */
+ PEData data;
+} BrushEdit;
+
+static int brush_edit_init(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+ ParticleEditSettings *pset= PE_settings(scene);
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ ARegion *ar= CTX_wm_region(C);
+ BrushEdit *bedit;
+ float min[3], max[3];
+
+ if (pset->brushtype < 0)
+ return 0;
+
+ /* set the 'distance factor' for grabbing (used in comb etc) */
+ INIT_MINMAX(min, max);
+ PE_minmax(scene, min, max);
+ mid_v3_v3v3(min, min, max);
+
+ bedit= MEM_callocN(sizeof(BrushEdit), "BrushEdit");
+ bedit->first= 1;
+ op->customdata= bedit;
+
+ bedit->scene= scene;
+ bedit->ob= ob;
+ bedit->edit= edit;
+
+ bedit->zfac = ED_view3d_calc_zfac(ar->regiondata, min, NULL);
+
+ /* cache view depths and settings for re-use */
+ PE_set_view3d_data(C, &bedit->data);
+
+ return 1;
+}
+
+static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+{
+ BrushEdit *bedit= op->customdata;
+ Scene *scene= bedit->scene;
+ Object *ob= bedit->ob;
+ PTCacheEdit *edit= bedit->edit;
+ ParticleEditSettings *pset= PE_settings(scene);
+ ParticleSystemModifierData *psmd= edit->psys ? psys_get_modifier(ob, edit->psys) : NULL;
+ ParticleBrushData *brush= &pset->brush[pset->brushtype];
+ ARegion *ar= CTX_wm_region(C);
+ float vec[3], mousef[2];
+ int mval[2];
+ int flip, mouse[2], removed= 0, added=0, selected= 0, tot_steps= 1, step= 1;
+ float dx, dy, dmax;
+ int lock_root = pset->flag & PE_LOCK_FIRST;
+
+ if (!PE_start_edit(edit))
+ return;
+
+ RNA_float_get_array(itemptr, "mouse", mousef);
+ mouse[0] = mousef[0];
+ mouse[1] = mousef[1];
+ flip= RNA_boolean_get(itemptr, "pen_flip");
+
+ if (bedit->first) {
+ bedit->lastmouse[0] = mouse[0];
+ bedit->lastmouse[1] = mouse[1];
+ }
+
+ dx= mouse[0] - bedit->lastmouse[0];
+ dy= mouse[1] - bedit->lastmouse[1];
+
+ mval[0] = mouse[0];
+ mval[1] = mouse[1];
+
+
+ /* disable locking temporatily for disconnected hair */
+ if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
+ pset->flag &= ~PE_LOCK_FIRST;
+
+ if (((pset->brushtype == PE_BRUSH_ADD) ?
+ (sqrtf(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) : (dx != 0 || dy != 0)) || bedit->first)
+ {
+ PEData data= bedit->data;
+
+ view3d_operator_needs_opengl(C);
+ selected= (short)count_selected_keys(scene, edit);
+
+ dmax = max_ff(fabsf(dx), fabsf(dy));
+ tot_steps = dmax/(0.2f * pe_brush_size_get(scene, brush)) + 1;
+
+ dx /= (float)tot_steps;
+ dy /= (float)tot_steps;
+
+ for (step = 1; step<=tot_steps; step++) {
+ mval[0] = bedit->lastmouse[0] + step*dx;
+ mval[1] = bedit->lastmouse[1] + step*dy;
+
+ switch (pset->brushtype) {
+ case PE_BRUSH_COMB:
+ {
+ const float mval_f[2] = {dx, dy};
+ data.mval= mval;
+ data.rad= pe_brush_size_get(scene, brush);
+
+ data.combfac= (brush->strength - 0.5f) * 2.0f;
+ if (data.combfac < 0.0f)
+ data.combfac= 1.0f - 9.0f * data.combfac;
+ else
+ data.combfac= 1.0f - data.combfac;
+
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ ED_view3d_win_to_delta(ar, mval_f, vec, bedit->zfac);
+ data.dvec= vec;
+
+ foreach_mouse_hit_key(&data, brush_comb, selected);
+ break;
+ }
+ case PE_BRUSH_CUT:
+ {
+ if (edit->psys && edit->pathcache) {
+ data.mval= mval;
+ data.rad= pe_brush_size_get(scene, brush);
+ data.cutfac= brush->strength;
+
+ if (selected)
+ foreach_selected_point(&data, brush_cut);
+ else
+ foreach_point(&data, brush_cut);
+
+ removed= remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
+ if (pset->flag & PE_KEEP_LENGTHS)
+ recalc_lengths(edit);
+ }
+ else
+ removed= 0;
+
+ break;
+ }
+ case PE_BRUSH_LENGTH:
+ {
+ data.mval= mval;
+
+ data.rad= pe_brush_size_get(scene, brush);
+ data.growfac= brush->strength / 50.0f;
+
+ if (brush->invert ^ flip)
+ data.growfac= 1.0f - data.growfac;
+ else
+ data.growfac= 1.0f + data.growfac;
+
+ foreach_mouse_hit_point(&data, brush_length, selected);
+
+ if (pset->flag & PE_KEEP_LENGTHS)
+ recalc_lengths(edit);
+ break;
+ }
+ case PE_BRUSH_PUFF:
+ {
+ if (edit->psys) {
+ data.dm= psmd->dm_final;
+ data.mval= mval;
+ data.rad= pe_brush_size_get(scene, brush);
+ data.select= selected;
+
+ data.pufffac= (brush->strength - 0.5f) * 2.0f;
+ if (data.pufffac < 0.0f)
+ data.pufffac= 1.0f - 9.0f * data.pufffac;
+ else
+ data.pufffac= 1.0f - data.pufffac;
+
+ data.invert= (brush->invert ^ flip);
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ foreach_mouse_hit_point(&data, brush_puff, selected);
+ }
+ break;
+ }
+ case PE_BRUSH_ADD:
+ {
+ if (edit->psys && edit->psys->part->from==PART_FROM_FACE) {
+ data.mval= mval;
+
+ added= brush_add(&data, brush->count);
+
+ if (pset->flag & PE_KEEP_LENGTHS)
+ recalc_lengths(edit);
+ }
+ else
+ added= 0;
+ break;
+ }
+ case PE_BRUSH_SMOOTH:
+ {
+ data.mval= mval;
+ data.rad= pe_brush_size_get(scene, brush);
+
+ data.vec[0] = data.vec[1] = data.vec[2] = 0.0f;
+ data.tot= 0;
+
+ data.smoothfac= brush->strength;
+
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ foreach_mouse_hit_key(&data, brush_smooth_get, selected);
+
+ if (data.tot) {
+ mul_v3_fl(data.vec, 1.0f / (float)data.tot);
+ foreach_mouse_hit_key(&data, brush_smooth_do, selected);
+ }
+
+ break;
+ }
+ case PE_BRUSH_WEIGHT:
+ {
+ if (edit->psys) {
+ data.dm= psmd->dm_final;
+ data.mval= mval;
+ data.rad= pe_brush_size_get(scene, brush);
+
+ data.weightfac = brush->strength; /* note that this will never be zero */
+
+ foreach_mouse_hit_key(&data, BKE_brush_weight_get, selected);
+ }
+
+ break;
+ }
+ }
+ if ((pset->flag & PE_KEEP_LENGTHS)==0)
+ recalc_lengths(edit);
+
+ if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
+ if (pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob))
+ PE_mirror_x(scene, ob, 1);
+
+ update_world_cos(ob, edit);
+ psys_free_path_cache(NULL, edit);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+ else
+ PE_update_object(scene, ob, 1);
+ }
+
+ if (edit->psys) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+ }
+ else {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ }
+
+ bedit->lastmouse[0] = mouse[0];
+ bedit->lastmouse[1] = mouse[1];
+ bedit->first= 0;
+ }
+
+ pset->flag |= lock_root;
+}
+
+static void brush_edit_exit(wmOperator *op)
+{
+ BrushEdit *bedit= op->customdata;
+
+ MEM_freeN(bedit);
+}
+
+static int brush_edit_exec(bContext *C, wmOperator *op)
+{
+ if (!brush_edit_init(C, op))
+ return OPERATOR_CANCELLED;
+
+ RNA_BEGIN (op->ptr, itemptr, "stroke")
+ {
+ brush_edit_apply(C, op, &itemptr);
+ }
+ RNA_END;
+
+ brush_edit_exit(op);
+
+ return OPERATOR_FINISHED;
+}
+
+static void brush_edit_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ PointerRNA itemptr;
+ float mouse[2];
+
+ VECCOPY2D(mouse, event->mval);
+
+ /* fill in stroke */
+ RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+ RNA_float_set_array(&itemptr, "mouse", mouse);
+ RNA_boolean_set(&itemptr, "pen_flip", event->shift != false); // XXX hardcoded
+
+ /* apply */
+ brush_edit_apply(C, op, &itemptr);
+}
+
+static int brush_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!brush_edit_init(C, op))
+ return OPERATOR_CANCELLED;
+
+ brush_edit_apply_event(C, op, event);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int brush_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ switch (event->type) {
+ case LEFTMOUSE:
+ case MIDDLEMOUSE:
+ case RIGHTMOUSE: // XXX hardcoded
+ brush_edit_exit(op);
+ return OPERATOR_FINISHED;
+ case MOUSEMOVE:
+ brush_edit_apply_event(C, op, event);
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void brush_edit_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ brush_edit_exit(op);
+}
+
+void PARTICLE_OT_brush_edit(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Brush Edit";
+ ot->idname = "PARTICLE_OT_brush_edit";
+ ot->description = "Apply a stroke of brush to the particles";
+
+ /* api callbacks */
+ ot->exec = brush_edit_exec;
+ ot->invoke = brush_edit_invoke;
+ ot->modal = brush_edit_modal;
+ ot->cancel = brush_edit_cancel;
+ ot->poll = PE_poll_view3d;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
+
+ /* properties */
+ RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+}
+
+/*********************** cut shape ***************************/
+
+static int shape_cut_poll(bContext *C)
+{
+ if (PE_hair_poll(C)) {
+ Scene *scene = CTX_data_scene(C);
+ ParticleEditSettings *pset = PE_settings(scene);
+
+ if (pset->shape_object && (pset->shape_object->type == OB_MESH)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+typedef struct PointInsideBVH {
+ BVHTreeFromMesh bvhdata;
+ int num_hits;
+} PointInsideBVH;
+
+static void point_inside_bvh_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
+{
+ PointInsideBVH *data = userdata;
+
+ data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit);
+
+ if (hit->index != -1)
+ ++data->num_hits;
+}
+
+/* true if the point is inside the shape mesh */
+static bool shape_cut_test_point(PEData *data, ParticleCacheKey *key)
+{
+ BVHTreeFromMesh *shape_bvh = &data->shape_bvh;
+ const float dir[3] = {1.0f, 0.0f, 0.0f};
+ PointInsideBVH userdata;
+
+ userdata.bvhdata = data->shape_bvh;
+ userdata.num_hits = 0;
+
+ BLI_bvhtree_ray_cast_all(
+ shape_bvh->tree, key->co, dir, 0.0f, BVH_RAYCAST_DIST_MAX,
+ point_inside_bvh_cb, &userdata);
+
+ /* for any point inside a watertight mesh the number of hits is uneven */
+ return (userdata.num_hits % 2) == 1;
+}
+
+static void shape_cut(PEData *data, int pa_index)
+{
+ PTCacheEdit *edit = data->edit;
+ Object *ob = data->ob;
+ ParticleEditSettings *pset = PE_settings(data->scene);
+ ParticleCacheKey *key;
+
+ bool cut;
+ float cut_time = 1.0;
+ int k, totkeys = 1 << pset->draw_step;
+
+ /* don't cut hidden */
+ if (edit->points[pa_index].flag & PEP_HIDE)
+ return;
+
+ cut = false;
+
+ /* check if root is inside the cut shape */
+ key = edit->pathcache[pa_index];
+ if (!shape_cut_test_point(data, key)) {
+ cut_time = -1.0f;
+ cut = true;
+ }
+ else {
+ for (k = 0; k < totkeys; k++, key++) {
+ BVHTreeRayHit hit;
+ float dir[3];
+ float len;
+
+ sub_v3_v3v3(dir, (key+1)->co, key->co);
+ len = normalize_v3(dir);
+
+ memset(&hit, 0, sizeof(hit));
+ hit.index = -1;
+ hit.dist = len;
+ BLI_bvhtree_ray_cast(data->shape_bvh.tree, key->co, dir, 0.0f, &hit, data->shape_bvh.raycast_callback, &data->shape_bvh);
+ if (hit.index >= 0) {
+ if (hit.dist < len) {
+ cut_time = (hit.dist / len + (float)k) / (float)totkeys;
+ cut = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cut) {
+ if (cut_time < 0.0f) {
+ edit->points[pa_index].flag |= PEP_TAG;
+ }
+ else {
+ rekey_particle_to_time(data->scene, ob, pa_index, cut_time);
+ edit->points[pa_index].flag |= PEP_EDIT_RECALC;
+ }
+ }
+}
+
+static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ ParticleEditSettings *pset = PE_settings(scene);
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ Object *shapeob = pset->shape_object;
+ int selected = count_selected_keys(scene, edit);
+ int lock_root = pset->flag & PE_LOCK_FIRST;
+
+ if (!PE_start_edit(edit))
+ return OPERATOR_CANCELLED;
+
+ /* disable locking temporatily for disconnected hair */
+ if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
+ pset->flag &= ~PE_LOCK_FIRST;
+
+ if (edit->psys && edit->pathcache) {
+ PEData data;
+ int removed;
+
+ PE_set_data(C, &data);
+ if (!PE_create_shape_tree(&data, shapeob)) {
+ /* shapeob may not have faces... */
+ return OPERATOR_CANCELLED;
+ }
+
+ if (selected)
+ foreach_selected_point(&data, shape_cut);
+ else
+ foreach_point(&data, shape_cut);
+
+ removed = remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
+ recalc_lengths(edit);
+
+ if (removed) {
+ update_world_cos(ob, edit);
+ psys_free_path_cache(NULL, edit);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+ else
+ PE_update_object(scene, ob, 1);
+
+ if (edit->psys) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+ }
+ else {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ }
+
+ PE_free_shape_tree(&data);
+ }
+
+ pset->flag |= lock_root;
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_shape_cut(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Shape Cut";
+ ot->idname = "PARTICLE_OT_shape_cut";
+ ot->description = "Cut hair to conform to the set shape object";
+
+ /* api callbacks */
+ ot->exec = shape_cut_exec;
+ ot->poll = shape_cut_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/*********************** undo ***************************/
+
+static void free_PTCacheUndo(PTCacheUndo *undo)
+{
+ PTCacheEditPoint *point;
+ int i;
+
+ for (i=0, point=undo->points; i<undo->totpoint; i++, point++) {
+ if (undo->particles && (undo->particles + i)->hair)
+ MEM_freeN((undo->particles + i)->hair);
+ if (point->keys)
+ MEM_freeN(point->keys);
+ }
+ if (undo->points)
+ MEM_freeN(undo->points);
+
+ if (undo->particles)
+ MEM_freeN(undo->particles);
+
+ BKE_ptcache_free_mem(&undo->mem_cache);
+}
+
+static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
+{
+ PTCacheEditPoint *point;
+ int i;
+
+ undo->totpoint= edit->totpoint;
+
+ if (edit->psys) {
+ ParticleData *pa;
+
+ pa= undo->particles= MEM_dupallocN(edit->psys->particles);
+
+ for (i=0; i<edit->totpoint; i++, pa++)
+ pa->hair= MEM_dupallocN(pa->hair);
+
+ undo->psys_flag = edit->psys->flag;
+ }
+ else {
+ PTCacheMem *pm;
+
+ BLI_duplicatelist(&undo->mem_cache, &edit->pid.cache->mem_cache);
+ pm = undo->mem_cache.first;
+
+ for (; pm; pm=pm->next) {
+ for (i=0; i<BPHYS_TOT_DATA; i++)
+ pm->data[i] = MEM_dupallocN(pm->data[i]);
+ }
+ }
+
+ point= undo->points = MEM_dupallocN(edit->points);
+ undo->totpoint = edit->totpoint;
+
+ for (i=0; i<edit->totpoint; i++, point++) {
+ point->keys= MEM_dupallocN(point->keys);
+ /* no need to update edit key->co & key->time pointers here */
+ }
+}
+
+static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
+{
+ ParticleSystem *psys = edit->psys;
+ ParticleData *pa;
+ HairKey *hkey;
+ POINT_P; KEY_K;
+
+ LOOP_POINTS {
+ if (psys && psys->particles[p].hair)
+ MEM_freeN(psys->particles[p].hair);
+
+ if (point->keys)
+ MEM_freeN(point->keys);
+ }
+ if (psys && psys->particles)
+ MEM_freeN(psys->particles);
+ if (edit->points)
+ MEM_freeN(edit->points);
+ if (edit->mirror_cache) {
+ MEM_freeN(edit->mirror_cache);
+ edit->mirror_cache= NULL;
+ }
+
+ edit->points= MEM_dupallocN(undo->points);
+ edit->totpoint = undo->totpoint;
+
+ LOOP_POINTS {
+ point->keys= MEM_dupallocN(point->keys);
+ }
+
+ if (psys) {
+ psys->particles= MEM_dupallocN(undo->particles);
+
+ psys->totpart= undo->totpoint;
+
+ LOOP_POINTS {
+ pa = psys->particles + p;
+ hkey= pa->hair = MEM_dupallocN(pa->hair);
+
+ LOOP_KEYS {
+ key->co= hkey->co;
+ key->time= &hkey->time;
+ hkey++;
+ }
+ }
+
+ psys->flag = undo->psys_flag;
+ }
+ else {
+ PTCacheMem *pm;
+ int i;
+
+ BKE_ptcache_free_mem(&edit->pid.cache->mem_cache);
+
+ BLI_duplicatelist(&edit->pid.cache->mem_cache, &undo->mem_cache);
+
+ pm = edit->pid.cache->mem_cache.first;
+
+ for (; pm; pm=pm->next) {
+ for (i=0; i<BPHYS_TOT_DATA; i++)
+ pm->data[i] = MEM_dupallocN(pm->data[i]);
+
+ BKE_ptcache_mem_pointers_init(pm);
+
+ LOOP_POINTS {
+ LOOP_KEYS {
+ if ((int)key->ftime == (int)pm->frame) {
+ key->co = pm->cur[BPHYS_DATA_LOCATION];
+ key->vel = pm->cur[BPHYS_DATA_VELOCITY];
+ key->rot = pm->cur[BPHYS_DATA_ROTATION];
+ key->time = &key->ftime;
+ }
+ }
+ BKE_ptcache_mem_pointers_incr(pm);
+ }
+ }
+ }
+}
+
+void PE_undo_push(Scene *scene, const char *str)
+{
+ PTCacheEdit *edit= PE_get_current(scene, OBACT);
+ PTCacheUndo *undo;
+ int nr;
+
+ if (!edit) return;
+
+ /* remove all undos after (also when curundo==NULL) */
+ while (edit->undo.last != edit->curundo) {
+ undo= edit->undo.last;
+ BLI_remlink(&edit->undo, undo);
+ free_PTCacheUndo(undo);
+ MEM_freeN(undo);
+ }
+
+ /* make new */
+ edit->curundo= undo= MEM_callocN(sizeof(PTCacheUndo), "particle undo file");
+ BLI_strncpy(undo->name, str, sizeof(undo->name));
+ BLI_addtail(&edit->undo, undo);
+
+ /* and limit amount to the maximum */
+ nr= 0;
+ undo= edit->undo.last;
+ while (undo) {
+ nr++;
+ if (nr==U.undosteps) break;
+ undo= undo->prev;
+ }
+ if (undo) {
+ while (edit->undo.first!=undo) {
+ PTCacheUndo *first= edit->undo.first;
+ BLI_remlink(&edit->undo, first);
+ free_PTCacheUndo(first);
+ MEM_freeN(first);
+ }
+ }
+
+ /* copy */
+ make_PTCacheUndo(edit, edit->curundo);
+}
+
+void PE_undo_step(Scene *scene, int step)
+{
+ PTCacheEdit *edit= PE_get_current(scene, OBACT);
+
+ if (!edit) return;
+
+ if (step==0) {
+ get_PTCacheUndo(edit, edit->curundo);
+ }
+ else if (step==1) {
+
+ if (edit->curundo==NULL || edit->curundo->prev==NULL) {
+ /* pass */
+ }
+ else {
+ if (G.debug & G_DEBUG) printf("undo %s\n", edit->curundo->name);
+ edit->curundo= edit->curundo->prev;
+ get_PTCacheUndo(edit, edit->curundo);
+ }
+ }
+ else {
+ /* curundo has to remain current situation! */
+
+ if (edit->curundo==NULL || edit->curundo->next==NULL) {
+ /* pass */
+ }
+ else {
+ get_PTCacheUndo(edit, edit->curundo->next);
+ edit->curundo= edit->curundo->next;
+ if (G.debug & G_DEBUG) printf("redo %s\n", edit->curundo->name);
+ }
+ }
+
+ DAG_id_tag_update(&OBACT->id, OB_RECALC_DATA);
+}
+
+bool PE_undo_is_valid(Scene *scene)
+{
+ PTCacheEdit *edit= PE_get_current(scene, OBACT);
+
+ if (edit) {
+ return (edit->undo.last != edit->undo.first);
+ }
+ return 0;
+}
+
+void PTCacheUndo_clear(PTCacheEdit *edit)
+{
+ PTCacheUndo *undo;
+
+ if (edit==NULL) return;
+
+ undo= edit->undo.first;
+ while (undo) {
+ free_PTCacheUndo(undo);
+ undo= undo->next;
+ }
+ BLI_freelistN(&edit->undo);
+ edit->curundo= NULL;
+}
+
+void PE_undo(Scene *scene)
+{
+ PE_undo_step(scene, 1);
+}
+
+void PE_redo(Scene *scene)
+{
+ PE_undo_step(scene, -1);
+}
+
+void PE_undo_number(Scene *scene, int nr)
+{
+ PTCacheEdit *edit= PE_get_current(scene, OBACT);
+ PTCacheUndo *undo;
+ int a=0;
+
+ for (undo= edit->undo.first; undo; undo= undo->next, a++) {
+ if (a==nr) break;
+ }
+ edit->curundo= undo;
+ PE_undo_step(scene, 0);
+}
+
+
+/* get name of undo item, return null if no item with this index */
+/* if active pointer, set it to 1 if true */
+const char *PE_undo_get_name(Scene *scene, int nr, bool *r_active)
+{
+ PTCacheEdit *edit= PE_get_current(scene, OBACT);
+ PTCacheUndo *undo;
+
+ if (r_active) *r_active = false;
+
+ if (edit) {
+ undo= BLI_findlink(&edit->undo, nr);
+ if (undo) {
+ if (r_active && (undo == edit->curundo)) {
+ *r_active = true;
+ }
+ return undo->name;
+ }
+ }
+ return NULL;
+}
+
+/************************ utilities ******************************/
+
+int PE_minmax(Scene *scene, float min[3], float max[3])
+{
+ Object *ob= OBACT;
+ PTCacheEdit *edit= PE_get_current(scene, ob);
+ ParticleSystem *psys;
+ ParticleSystemModifierData *psmd = NULL;
+ POINT_P; KEY_K;
+ float co[3], mat[4][4];
+ int ok= 0;
+
+ if (!edit) return ok;
+
+ if ((psys = edit->psys))
+ psmd= psys_get_modifier(ob, psys);
+ else
+ unit_m4(mat);
+
+ LOOP_VISIBLE_POINTS {
+ if (psys)
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles+p, mat);
+
+ LOOP_SELECTED_KEYS {
+ copy_v3_v3(co, key->co);
+ mul_m4_v3(mat, co);
+ DO_MINMAX(co, min, max);
+ ok= 1;
+ }
+ }
+
+ if (!ok) {
+ BKE_object_minmax(ob, min, max, true);
+ ok= 1;
+ }
+
+ return ok;
+}
+
+/************************ particle edit toggle operator ************************/
+
+/* initialize needed data for bake edit */
+void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
+{
+ PTCacheEdit *edit;
+ ParticleSystemModifierData *psmd = (psys) ? psys_get_modifier(ob, psys) : NULL;
+ POINT_P; KEY_K;
+ ParticleData *pa = NULL;
+ HairKey *hkey;
+ int totpoint;
+
+ /* no psmd->dm happens in case particle system modifier is not enabled */
+ if (!(psys && psmd && psmd->dm_final) && !cache)
+ return;
+
+ if (cache && cache->flag & PTCACHE_DISK_CACHE)
+ return;
+
+ if (psys == NULL && (cache && BLI_listbase_is_empty(&cache->mem_cache)))
+ return;
+
+ edit = (psys) ? psys->edit : cache->edit;
+
+ if (!edit) {
+ totpoint = psys ? psys->totpart : (int)((PTCacheMem *)cache->mem_cache.first)->totpoint;
+
+ edit= MEM_callocN(sizeof(PTCacheEdit), "PE_create_particle_edit");
+ edit->points=MEM_callocN(totpoint*sizeof(PTCacheEditPoint), "PTCacheEditPoints");
+ edit->totpoint = totpoint;
+
+ if (psys && !cache) {
+ psys->edit= edit;
+ edit->psys = psys;
+
+ psys->free_edit= PE_free_ptcache_edit;
+
+ edit->pathcache = NULL;
+ BLI_listbase_clear(&edit->pathcachebufs);
+
+ pa = psys->particles;
+ LOOP_POINTS {
+ point->totkey = pa->totkey;
+ point->keys= MEM_callocN(point->totkey*sizeof(PTCacheEditKey), "ParticleEditKeys");
+ point->flag |= PEP_EDIT_RECALC;
+
+ hkey = pa->hair;
+ LOOP_KEYS {
+ key->co= hkey->co;
+ key->time= &hkey->time;
+ key->flag= hkey->editflag;
+ if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
+ key->flag |= PEK_USE_WCO;
+ hkey->editflag |= PEK_USE_WCO;
+ }
+
+ hkey++;
+ }
+ pa++;
+ }
+ update_world_cos(ob, edit);
+ }
+ else {
+ PTCacheMem *pm;
+ int totframe=0;
+
+ cache->edit= edit;
+ cache->free_edit= PE_free_ptcache_edit;
+ edit->psys = NULL;
+
+ for (pm=cache->mem_cache.first; pm; pm=pm->next)
+ totframe++;
+
+ for (pm=cache->mem_cache.first; pm; pm=pm->next) {
+ LOOP_POINTS {
+ if (BKE_ptcache_mem_pointers_seek(p, pm) == 0)
+ continue;
+
+ if (!point->totkey) {
+ key = point->keys = MEM_callocN(totframe*sizeof(PTCacheEditKey), "ParticleEditKeys");
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ else
+ key = point->keys + point->totkey;
+
+ key->co = pm->cur[BPHYS_DATA_LOCATION];
+ key->vel = pm->cur[BPHYS_DATA_VELOCITY];
+ key->rot = pm->cur[BPHYS_DATA_ROTATION];
+ key->ftime = (float)pm->frame;
+ key->time = &key->ftime;
+ BKE_ptcache_mem_pointers_incr(pm);
+
+ point->totkey++;
+ }
+ }
+ psys = NULL;
+ }
+
+ UI_GetThemeColor3ubv(TH_EDGE_SELECT, edit->sel_col);
+ UI_GetThemeColor3ubv(TH_WIRE, edit->nosel_col);
+
+ recalc_lengths(edit);
+ if (psys && !cache)
+ recalc_emitter_field(ob, psys);
+ PE_update_object(scene, ob, 1);
+
+ PTCacheUndo_clear(edit);
+ PE_undo_push(scene, "Original");
+ }
+}
+
+static int particle_edit_toggle_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob == NULL || ob->type != OB_MESH)
+ return 0;
+ if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data))
+ return 0;
+ if (CTX_data_edit_object(C))
+ return 0;
+
+ return (ob->particlesystem.first ||
+ modifiers_findByType(ob, eModifierType_Cloth) ||
+ modifiers_findByType(ob, eModifierType_Softbody));
+}
+
+static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ const int mode_flag = OB_MODE_PARTICLE_EDIT;
+ const bool is_mode_set = (ob->mode & mode_flag) != 0;
+
+ if (!is_mode_set) {
+ if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ if (!is_mode_set) {
+ PTCacheEdit *edit;
+ ob->mode |= mode_flag;
+ edit= PE_create_current(scene, ob);
+
+ /* mesh may have changed since last entering editmode.
+ * note, this may have run before if the edit data was just created, so could avoid this and speed up a little */
+ if (edit && edit->psys)
+ recalc_emitter_field(ob, edit->psys);
+
+ toggle_particle_cursor(C, 1);
+ WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL);
+ }
+ else {
+ ob->mode &= ~mode_flag;
+ toggle_particle_cursor(C, 0);
+ WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_particle_edit_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Particle Edit Toggle";
+ ot->idname = "PARTICLE_OT_particle_edit_toggle";
+ ot->description = "Toggle particle edit mode";
+
+ /* api callbacks */
+ ot->exec = particle_edit_toggle_exec;
+ ot->poll = particle_edit_toggle_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+
+/************************ set editable operator ************************/
+
+static int clear_edited_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob= CTX_data_active_object(C);
+ ParticleSystem *psys = psys_get_current(ob);
+
+ if (psys->edit) {
+ if (psys->edit->edited || 1) {
+ PE_free_ptcache_edit(psys->edit);
+
+ psys->edit = NULL;
+ psys->free_edit = NULL;
+
+ psys->recalc |= PSYS_RECALC_RESET;
+ psys->flag &= ~PSYS_GLOBAL_HAIR;
+ psys->flag &= ~PSYS_EDITED;
+
+ psys_reset(psys, PSYS_RESET_DEPSGRAPH);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+ }
+ else { /* some operation might have protected hair from editing so let's clear the flag */
+ psys->recalc |= PSYS_RECALC_RESET;
+ psys->flag &= ~PSYS_GLOBAL_HAIR;
+ psys->flag &= ~PSYS_EDITED;
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int clear_edited_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ return WM_operator_confirm_message(C, op, "Lose changes done in particle mode? (no undo)");
+}
+
+void PARTICLE_OT_edited_clear(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Clear Edited";
+ ot->idname = "PARTICLE_OT_edited_clear";
+ ot->description = "Undo all edition performed on the particle system";
+
+ /* api callbacks */
+ ot->exec = clear_edited_exec;
+ ot->poll = particle_edit_toggle_poll;
+ ot->invoke = clear_edited_invoke;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ Unify length operator ************************/
+
+static float calculate_point_length(PTCacheEditPoint *point)
+{
+ float length = 0.0f;
+ KEY_K;
+ LOOP_KEYS {
+ if (k > 0) {
+ length += len_v3v3((key - 1)->co, key->co);
+ }
+ }
+ return length;
+}
+
+static float calculate_average_length(PTCacheEdit *edit)
+{
+ int num_selected = 0;
+ float total_length = 0;
+ POINT_P;
+ LOOP_SELECTED_POINTS {
+ total_length += calculate_point_length(point);
+ ++num_selected;
+ }
+ if (num_selected == 0) {
+ return 0.0f;
+ }
+ return total_length / num_selected;
+}
+
+static void scale_point_factor(PTCacheEditPoint *point, float factor)
+{
+ float orig_prev_co[3], prev_co[3];
+ KEY_K;
+ LOOP_KEYS {
+ if (k == 0) {
+ copy_v3_v3(orig_prev_co, key->co);
+ copy_v3_v3(prev_co, key->co);
+ }
+ else {
+ float new_co[3];
+ float delta[3];
+
+ sub_v3_v3v3(delta, key->co, orig_prev_co);
+ mul_v3_fl(delta, factor);
+ add_v3_v3v3(new_co, prev_co, delta);
+
+ copy_v3_v3(orig_prev_co, key->co);
+ copy_v3_v3(key->co, new_co);
+ copy_v3_v3(prev_co, key->co);
+ }
+ }
+ point->flag |= PEP_EDIT_RECALC;
+}
+
+static void scale_point_to_length(PTCacheEditPoint *point, float length)
+{
+ const float point_length = calculate_point_length(point);
+ if (point_length != 0.0f) {
+ const float factor = length / point_length;
+ scale_point_factor(point, factor);
+ }
+}
+
+static void scale_points_to_length(PTCacheEdit *edit, float length)
+{
+ POINT_P;
+ LOOP_SELECTED_POINTS {
+ scale_point_to_length(point, length);
+ }
+ recalc_lengths(edit);
+}
+
+static int unify_length_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+ Scene *scene = CTX_data_scene(C);
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ float average_length = calculate_average_length(edit);
+ if (average_length == 0.0f) {
+ return OPERATOR_CANCELLED;
+ }
+ scale_points_to_length(edit, average_length);
+
+ PE_update_object(scene, ob, 1);
+ if (edit->psys) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+ }
+ else {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_unify_length(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Unify Length";
+ ot->idname = "PARTICLE_OT_unify_length";
+ ot->description = "Make selected hair the same length";
+
+ /* api callbacks */
+ ot->exec = unify_length_exec;
+ ot->poll = PE_poll_view3d;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
new file mode 100644
index 00000000000..4a4474868a2
--- /dev/null
+++ b/source/blender/editors/physics/particle_object.c
@@ -0,0 +1,1244 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/physics/particle_object.c
+ * \ingroup edphys
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_math.h"
+#include "BLI_listbase.h"
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_report.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_particle.h"
+#include "ED_screen.h"
+#include "ED_object.h"
+
+#include "UI_resources.h"
+
+#include "physics_intern.h"
+
+extern void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
+extern void PTCacheUndo_clear(PTCacheEdit *edit);
+extern void recalc_lengths(PTCacheEdit *edit);
+extern void recalc_emitter_field(Object *ob, ParticleSystem *psys);
+extern void update_world_cos(Object *ob, PTCacheEdit *edit);
+
+#define KEY_K PTCacheEditKey *key; int k
+#define POINT_P PTCacheEditPoint *point; int p
+#define LOOP_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++)
+#if 0
+#define LOOP_VISIBLE_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!(point->flag & PEP_HIDE))
+#define LOOP_SELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point_is_selected(point))
+#define LOOP_UNSELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!point_is_selected(point))
+#define LOOP_EDITED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_EDIT_RECALC)
+#define LOOP_TAGGED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_TAG)
+#endif
+#define LOOP_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++)
+#if 0
+#define LOOP_VISIBLE_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (!(key->flag & PEK_HIDE))
+#define LOOP_SELECTED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if ((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
+#define LOOP_TAGGED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (key->flag & PEK_TAG)
+
+#define KEY_WCO (key->flag & PEK_USE_WCO ? key->world_co : key->co)
+#endif
+
+static float I[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}};
+
+/********************** particle system slot operators *********************/
+
+static int particle_system_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob= ED_object_context(C);
+ Scene *scene = CTX_data_scene(C);
+
+ if (!scene || !ob)
+ return OPERATOR_CANCELLED;
+
+ object_add_particle_system(scene, ob, NULL);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_particle_system_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Particle System Slot";
+ ot->idname = "OBJECT_OT_particle_system_add";
+ ot->description = "Add a particle system";
+
+ /* api callbacks */
+ ot->poll = ED_operator_object_active_editable;
+ ot->exec = particle_system_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int particle_system_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ Scene *scene = CTX_data_scene(C);
+ int mode_orig;
+
+ if (!scene || !ob)
+ return OPERATOR_CANCELLED;
+
+ mode_orig = ob->mode;
+ object_remove_particle_system(scene, ob);
+
+ /* possible this isn't the active object
+ * object_remove_particle_system() clears the mode on the last psys
+ */
+ if (mode_orig & OB_MODE_PARTICLE_EDIT) {
+ if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
+ if (scene->basact && scene->basact->object == ob) {
+ WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Particle System Slot";
+ ot->idname = "OBJECT_OT_particle_system_remove";
+ ot->description = "Remove the selected particle system";
+
+ /* api callbacks */
+ ot->poll = ED_operator_object_active_editable;
+ ot->exec = particle_system_remove_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** new particle settings operator *********************/
+
+static int psys_poll(bContext *C)
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ return (ptr.data != NULL);
+}
+
+static int new_particle_settings_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain= CTX_data_main(C);
+ ParticleSystem *psys;
+ ParticleSettings *part = NULL;
+ Object *ob;
+ PointerRNA ptr;
+
+ ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+
+ psys = ptr.data;
+
+ /* add or copy particle setting */
+ if (psys->part)
+ part= BKE_particlesettings_copy(bmain, psys->part);
+ else
+ part= psys_new_settings("ParticleSettings", bmain);
+
+ ob= ptr.id.data;
+
+ if (psys->part)
+ id_us_min(&psys->part->id);
+
+ psys->part = part;
+
+ psys_check_boid_data(psys);
+
+ DAG_relations_tag_update(bmain);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_new(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Particle Settings";
+ ot->idname = "PARTICLE_OT_new";
+ ot->description = "Add new particle settings";
+
+ /* api callbacks */
+ ot->exec = new_particle_settings_exec;
+ ot->poll = psys_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** keyed particle target operators *********************/
+
+static int new_particle_target_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ Object *ob = ptr.id.data;
+
+ ParticleTarget *pt;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+
+ pt = psys->targets.first;
+ for (; pt; pt=pt->next)
+ pt->flag &= ~PTARGET_CURRENT;
+
+ pt = MEM_callocN(sizeof(ParticleTarget), "keyed particle target");
+
+ pt->flag |= PTARGET_CURRENT;
+ pt->psys = 1;
+
+ BLI_addtail(&psys->targets, pt);
+
+ DAG_relations_tag_update(bmain);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_new_target(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Particle Target";
+ ot->idname = "PARTICLE_OT_new_target";
+ ot->description = "Add a new particle target";
+
+ /* api callbacks */
+ ot->exec = new_particle_target_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int remove_particle_target_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ Object *ob = ptr.id.data;
+
+ ParticleTarget *pt;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+
+ pt = psys->targets.first;
+ for (; pt; pt=pt->next) {
+ if (pt->flag & PTARGET_CURRENT) {
+ BLI_remlink(&psys->targets, pt);
+ MEM_freeN(pt);
+ break;
+ }
+
+ }
+ pt = psys->targets.last;
+
+ if (pt)
+ pt->flag |= PTARGET_CURRENT;
+
+ DAG_relations_tag_update(bmain);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_target_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Particle Target";
+ ot->idname = "PARTICLE_OT_target_remove";
+ ot->description = "Remove the selected particle target";
+
+ /* api callbacks */
+ ot->exec = remove_particle_target_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move up particle target operator *********************/
+
+static int target_move_up_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ Object *ob = ptr.id.data;
+ ParticleTarget *pt;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+
+ pt = psys->targets.first;
+ for (; pt; pt=pt->next) {
+ if (pt->flag & PTARGET_CURRENT && pt->prev) {
+ BLI_remlink(&psys->targets, pt);
+ BLI_insertlinkbefore(&psys->targets, pt->prev, pt);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_target_move_up(wmOperatorType *ot)
+{
+ ot->name = "Move Up Target";
+ ot->idname = "PARTICLE_OT_target_move_up";
+ ot->description = "Move particle target up in the list";
+
+ ot->exec = target_move_up_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move down particle target operator *********************/
+
+static int target_move_down_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ Object *ob = ptr.id.data;
+ ParticleTarget *pt;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+ pt = psys->targets.first;
+ for (; pt; pt=pt->next) {
+ if (pt->flag & PTARGET_CURRENT && pt->next) {
+ BLI_remlink(&psys->targets, pt);
+ BLI_insertlinkafter(&psys->targets, pt->next, pt);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_target_move_down(wmOperatorType *ot)
+{
+ ot->name = "Move Down Target";
+ ot->idname = "PARTICLE_OT_target_move_down";
+ ot->description = "Move particle target down in the list";
+
+ ot->exec = target_move_down_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move up particle dupliweight operator *********************/
+
+static int dupliob_move_up_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ ParticleSettings *part;
+ ParticleDupliWeight *dw;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+
+ part = psys->part;
+ for (dw=part->dupliweights.first; dw; dw=dw->next) {
+ if (dw->flag & PART_DUPLIW_CURRENT && dw->prev) {
+ BLI_remlink(&part->dupliweights, dw);
+ BLI_insertlinkbefore(&part->dupliweights, dw->prev, dw);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_dupliob_move_up(wmOperatorType *ot)
+{
+ ot->name = "Move Up Dupli Object";
+ ot->idname = "PARTICLE_OT_dupliob_move_up";
+ ot->description = "Move dupli object up in the list";
+
+ ot->exec = dupliob_move_up_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** particle dupliweight operators *********************/
+
+static int copy_particle_dupliob_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ ParticleSettings *part;
+ ParticleDupliWeight *dw;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+ part = psys->part;
+ for (dw=part->dupliweights.first; dw; dw=dw->next) {
+ if (dw->flag & PART_DUPLIW_CURRENT) {
+ dw->flag &= ~PART_DUPLIW_CURRENT;
+ dw = MEM_dupallocN(dw);
+ dw->flag |= PART_DUPLIW_CURRENT;
+ BLI_addhead(&part->dupliweights, dw);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_dupliob_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Particle Dupliob";
+ ot->idname = "PARTICLE_OT_dupliob_copy";
+ ot->description = "Duplicate the current dupliobject";
+
+ /* api callbacks */
+ ot->exec = copy_particle_dupliob_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int remove_particle_dupliob_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ ParticleSettings *part;
+ ParticleDupliWeight *dw;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+
+ part = psys->part;
+ for (dw=part->dupliweights.first; dw; dw=dw->next) {
+ if (dw->flag & PART_DUPLIW_CURRENT) {
+ BLI_remlink(&part->dupliweights, dw);
+ MEM_freeN(dw);
+ break;
+ }
+
+ }
+ dw = part->dupliweights.last;
+
+ if (dw)
+ dw->flag |= PART_DUPLIW_CURRENT;
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_dupliob_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Particle Dupliobject";
+ ot->idname = "PARTICLE_OT_dupliob_remove";
+ ot->description = "Remove the selected dupliobject";
+
+ /* api callbacks */
+ ot->exec = remove_particle_dupliob_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move down particle dupliweight operator *********************/
+
+static int dupliob_move_down_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
+ ParticleSystem *psys= ptr.data;
+ ParticleSettings *part;
+ ParticleDupliWeight *dw;
+
+ if (!psys)
+ return OPERATOR_CANCELLED;
+
+ part = psys->part;
+ for (dw=part->dupliweights.first; dw; dw=dw->next) {
+ if (dw->flag & PART_DUPLIW_CURRENT && dw->next) {
+ BLI_remlink(&part->dupliweights, dw);
+ BLI_insertlinkafter(&part->dupliweights, dw->next, dw);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, NULL);
+ break;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot)
+{
+ ot->name = "Move Down Dupli Object";
+ ot->idname = "PARTICLE_OT_dupliob_move_down";
+ ot->description = "Move dupli object down in the list";
+
+ ot->exec = dupliob_move_down_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ connect/disconnect hair operators *********************/
+
+static void disconnect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
+{
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ ParticleEditSettings *pset= PE_settings(scene);
+ ParticleData *pa;
+ PTCacheEdit *edit;
+ PTCacheEditPoint *point;
+ PTCacheEditKey *ekey = NULL;
+ HairKey *key;
+ int i, k;
+ float hairmat[4][4];
+
+ if (!ob || !psys || psys->flag & PSYS_GLOBAL_HAIR)
+ return;
+
+ if (!psys->part || psys->part->type != PART_HAIR)
+ return;
+
+ edit = psys->edit;
+ point= edit ? edit->points : NULL;
+
+ for (i=0, pa=psys->particles; i<psys->totpart; i++, pa++) {
+ if (point) {
+ ekey = point->keys;
+ point++;
+ }
+
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, pa, hairmat);
+
+ for (k=0, key=pa->hair; k<pa->totkey; k++, key++) {
+ mul_m4_v3(hairmat, key->co);
+
+ if (ekey) {
+ ekey->flag &= ~PEK_USE_WCO;
+ ekey++;
+ }
+ }
+ }
+
+ psys_free_path_cache(psys, psys->edit);
+
+ psys->flag |= PSYS_GLOBAL_HAIR;
+
+ if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_PUFF))
+ pset->brushtype = PE_BRUSH_NONE;
+
+ PE_update_object(scene, ob, 0);
+}
+
+static int disconnect_hair_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ED_object_context(C);
+ ParticleSystem *psys= NULL;
+ const bool all = RNA_boolean_get(op->ptr, "all");
+
+ if (!ob)
+ return OPERATOR_CANCELLED;
+
+ if (all) {
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ disconnect_hair(scene, ob, psys);
+ }
+ }
+ else {
+ psys = psys_get_current(ob);
+ disconnect_hair(scene, ob, psys);
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
+{
+ ot->name = "Disconnect Hair";
+ ot->description = "Disconnect hair from the emitter mesh";
+ ot->idname = "PARTICLE_OT_disconnect_hair";
+
+ ot->exec = disconnect_hair_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO; /* No REGISTER, redo does not work due to missing update, see T47750. */
+
+ RNA_def_boolean(ot->srna, "all", 0, "All hair", "Disconnect all hair systems from the emitter mesh");
+}
+
+/* from/to_world_space : whether from/to particles are in world or hair space
+ * from/to_mat : additional transform for from/to particles (e.g. for using object space copying)
+ */
+static bool remap_hair_emitter(Scene *scene, Object *ob, ParticleSystem *psys,
+ Object *target_ob, ParticleSystem *target_psys, PTCacheEdit *target_edit,
+ float from_mat[4][4], float to_mat[4][4], bool from_global, bool to_global)
+{
+ ParticleSystemModifierData *target_psmd = psys_get_modifier(target_ob, target_psys);
+ ParticleData *pa, *tpa;
+ PTCacheEditPoint *edit_point;
+ PTCacheEditKey *ekey;
+ BVHTreeFromMesh bvhtree= {NULL};
+ MFace *mface = NULL, *mf;
+ MEdge *medge = NULL, *me;
+ MVert *mvert;
+ DerivedMesh *dm, *target_dm;
+ int numverts;
+ int i, k;
+ float from_ob_imat[4][4], to_ob_imat[4][4];
+ float from_imat[4][4], to_imat[4][4];
+
+ if (!target_psmd->dm_final)
+ return false;
+ if (!psys->part || psys->part->type != PART_HAIR)
+ return false;
+ if (!target_psys->part || target_psys->part->type != PART_HAIR)
+ return false;
+
+ edit_point = target_edit ? target_edit->points : NULL;
+
+ invert_m4_m4(from_ob_imat, ob->obmat);
+ invert_m4_m4(to_ob_imat, target_ob->obmat);
+ invert_m4_m4(from_imat, from_mat);
+ invert_m4_m4(to_imat, to_mat);
+
+ if (target_psmd->dm_final->deformedOnly) {
+ /* we don't want to mess up target_psmd->dm when converting to global coordinates below */
+ dm = target_psmd->dm_final;
+ }
+ else {
+ dm = target_psmd->dm_deformed;
+ }
+ target_dm = target_psmd->dm_final;
+ if (dm == NULL) {
+ return false;
+ }
+ /* don't modify the original vertices */
+ dm = CDDM_copy(dm);
+
+ /* BMESH_ONLY, deform dm may not have tessface */
+ DM_ensure_tessface(dm);
+
+ numverts = dm->getNumVerts(dm);
+ mvert = dm->getVertArray(dm);
+
+ /* convert to global coordinates */
+ for (i=0; i<numverts; i++)
+ mul_m4_v3(to_mat, mvert[i].co);
+
+ if (dm->getNumTessFaces(dm) != 0) {
+ mface = dm->getTessFaceArray(dm);
+ bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
+ }
+ else if (dm->getNumEdges(dm) != 0) {
+ medge = dm->getEdgeArray(dm);
+ bvhtree_from_mesh_edges(&bvhtree, dm, 0.0, 2, 6);
+ }
+ else {
+ dm->release(dm);
+ return false;
+ }
+
+ for (i = 0, tpa = target_psys->particles, pa = psys->particles;
+ i < target_psys->totpart;
+ i++, tpa++, pa++) {
+
+ float from_co[3];
+ BVHTreeNearest nearest;
+
+ if (from_global)
+ mul_v3_m4v3(from_co, from_ob_imat, pa->hair[0].co);
+ else
+ mul_v3_m4v3(from_co, from_ob_imat, pa->hair[0].world_co);
+ mul_m4_v3(from_mat, from_co);
+
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+
+ BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
+
+ if (nearest.index == -1) {
+ if (G.debug & G_DEBUG)
+ printf("No nearest point found for hair root!");
+ continue;
+ }
+
+ if (mface) {
+ float v[4][3];
+
+ mf = &mface[nearest.index];
+
+ copy_v3_v3(v[0], mvert[mf->v1].co);
+ copy_v3_v3(v[1], mvert[mf->v2].co);
+ copy_v3_v3(v[2], mvert[mf->v3].co);
+ if (mf->v4) {
+ copy_v3_v3(v[3], mvert[mf->v4].co);
+ interp_weights_poly_v3(tpa->fuv, v, 4, nearest.co);
+ }
+ else
+ interp_weights_poly_v3(tpa->fuv, v, 3, nearest.co);
+ tpa->foffset = 0.0f;
+
+ tpa->num = nearest.index;
+ tpa->num_dmcache = psys_particle_dm_face_lookup(target_dm, dm, tpa->num, tpa->fuv, NULL);
+ }
+ else {
+ me = &medge[nearest.index];
+
+ tpa->fuv[1] = line_point_factor_v3(nearest.co,
+ mvert[me->v1].co,
+ mvert[me->v2].co);
+ tpa->fuv[0] = 1.0f - tpa->fuv[1];
+ tpa->fuv[2] = tpa->fuv[3] = 0.0f;
+ tpa->foffset = 0.0f;
+
+ tpa->num = nearest.index;
+ tpa->num_dmcache = -1;
+ }
+
+ /* translate hair keys */
+ {
+ HairKey *key, *tkey;
+ float hairmat[4][4], imat[4][4];
+ float offset[3];
+
+ if (to_global)
+ copy_m4_m4(imat, target_ob->obmat);
+ else {
+ /* note: using target_dm here, which is in target_ob object space and has full modifiers */
+ psys_mat_hair_to_object(target_ob, target_dm, target_psys->part->from, tpa, hairmat);
+ invert_m4_m4(imat, hairmat);
+ }
+ mul_m4_m4m4(imat, imat, to_imat);
+
+ /* offset in world space */
+ sub_v3_v3v3(offset, nearest.co, from_co);
+
+ if (edit_point) {
+ for (k=0, key=pa->hair, tkey=tpa->hair, ekey = edit_point->keys; k<tpa->totkey; k++, key++, tkey++, ekey++) {
+ float co_orig[3];
+
+ if (from_global)
+ mul_v3_m4v3(co_orig, from_ob_imat, key->co);
+ else
+ mul_v3_m4v3(co_orig, from_ob_imat, key->world_co);
+ mul_m4_v3(from_mat, co_orig);
+
+ add_v3_v3v3(tkey->co, co_orig, offset);
+
+ mul_m4_v3(imat, tkey->co);
+
+ ekey->flag |= PEK_USE_WCO;
+ }
+
+ edit_point++;
+ }
+ else {
+ for (k=0, key=pa->hair, tkey=tpa->hair; k<tpa->totkey; k++, key++, tkey++) {
+ float co_orig[3];
+
+ if (from_global)
+ mul_v3_m4v3(co_orig, from_ob_imat, key->co);
+ else
+ mul_v3_m4v3(co_orig, from_ob_imat, key->world_co);
+ mul_m4_v3(from_mat, co_orig);
+
+ add_v3_v3v3(tkey->co, co_orig, offset);
+
+ mul_m4_v3(imat, tkey->co);
+ }
+ }
+ }
+ }
+
+ free_bvhtree_from_mesh(&bvhtree);
+ dm->release(dm);
+
+ psys_free_path_cache(target_psys, target_edit);
+
+ PE_update_object(scene, target_ob, 0);
+
+ return true;
+}
+
+static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
+{
+ bool ok;
+
+ if (!psys)
+ return false;
+
+ ok = remap_hair_emitter(scene, ob, psys, ob, psys, psys->edit, ob->obmat, ob->obmat, psys->flag & PSYS_GLOBAL_HAIR, false);
+ psys->flag &= ~PSYS_GLOBAL_HAIR;
+
+ return ok;
+}
+
+static int connect_hair_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ED_object_context(C);
+ ParticleSystem *psys= NULL;
+ const bool all = RNA_boolean_get(op->ptr, "all");
+ bool any_connected = false;
+
+ if (!ob)
+ return OPERATOR_CANCELLED;
+
+ if (all) {
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ any_connected |= connect_hair(scene, ob, psys);
+ }
+ }
+ else {
+ psys = psys_get_current(ob);
+ any_connected |= connect_hair(scene, ob, psys);
+ }
+
+ if (!any_connected) {
+ BKE_report(op->reports, RPT_WARNING,
+ "No hair connected (can't connect hair if particle system modifier is disabled)");
+ return OPERATOR_CANCELLED;
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_connect_hair(wmOperatorType *ot)
+{
+ ot->name = "Connect Hair";
+ ot->description = "Connect hair to the emitter mesh";
+ ot->idname = "PARTICLE_OT_connect_hair";
+
+ ot->exec = connect_hair_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO; /* No REGISTER, redo does not work due to missing update, see T47750. */
+
+ RNA_def_boolean(ot->srna, "all", 0, "All hair", "Connect all hair systems to the emitter mesh");
+}
+
+/************************ particle system copy operator *********************/
+
+typedef enum eCopyParticlesSpace {
+ PAR_COPY_SPACE_OBJECT = 0,
+ PAR_COPY_SPACE_WORLD = 1,
+} eCopyParticlesSpace;
+
+static void copy_particle_edit(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystem *psys_from)
+{
+ PTCacheEdit *edit_from = psys_from->edit, *edit;
+ ParticleData *pa;
+ KEY_K;
+ POINT_P;
+
+ if (!edit_from)
+ return;
+
+ edit = MEM_dupallocN(edit_from);
+ edit->psys = psys;
+ psys->edit = edit;
+
+ edit->pathcache = NULL;
+ BLI_listbase_clear(&edit->pathcachebufs);
+
+ edit->emitter_field = NULL;
+ edit->emitter_cosnos = NULL;
+
+ BLI_listbase_clear(&edit->undo);
+ edit->curundo = NULL;
+
+ edit->points = MEM_dupallocN(edit_from->points);
+ pa = psys->particles;
+ LOOP_POINTS {
+ HairKey *hkey = pa->hair;
+
+ point->keys= MEM_dupallocN(point->keys);
+ LOOP_KEYS {
+ key->co = hkey->co;
+ key->time = &hkey->time;
+ key->flag = hkey->editflag;
+ if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
+ key->flag |= PEK_USE_WCO;
+ hkey->editflag |= PEK_USE_WCO;
+ }
+
+ hkey++;
+ }
+
+ pa++;
+ }
+ update_world_cos(ob, edit);
+
+ UI_GetThemeColor3ubv(TH_EDGE_SELECT, edit->sel_col);
+ UI_GetThemeColor3ubv(TH_WIRE, edit->nosel_col);
+
+ recalc_lengths(edit);
+ recalc_emitter_field(ob, psys);
+ PE_update_object(scene, ob, true);
+
+ PTCacheUndo_clear(edit);
+ PE_undo_push(scene, "Original");
+}
+
+static void remove_particle_systems_from_object(Object *ob_to)
+{
+ ModifierData *md, *md_next;
+
+ if (ob_to->type != OB_MESH)
+ return;
+ if (!ob_to->data || ID_IS_LINKED_DATABLOCK(ob_to->data))
+ return;
+
+ for (md = ob_to->modifiers.first; md; md = md_next) {
+ md_next = md->next;
+
+ /* remove all particle system modifiers as well,
+ * these need to sync to the particle system list
+ */
+ if (ELEM(md->type, eModifierType_ParticleSystem, eModifierType_DynamicPaint, eModifierType_Smoke)) {
+ BLI_remlink(&ob_to->modifiers, md);
+ modifier_free(md);
+ }
+ }
+
+ BKE_object_free_particlesystems(ob_to);
+}
+
+/* single_psys_from is optional, if NULL all psys of ob_from are copied */
+static bool copy_particle_systems_to_object(Main *bmain,
+ Scene *scene,
+ Object *ob_from,
+ ParticleSystem *single_psys_from,
+ Object *ob_to,
+ int space,
+ bool duplicate_settings)
+{
+ ModifierData *md;
+ ParticleSystem *psys_start = NULL, *psys, *psys_from;
+ ParticleSystem **tmp_psys;
+ DerivedMesh *final_dm;
+ CustomDataMask cdmask;
+ int i, totpsys;
+
+ if (ob_to->type != OB_MESH)
+ return false;
+ if (!ob_to->data || ID_IS_LINKED_DATABLOCK(ob_to->data))
+ return false;
+
+ /* For remapping we need a valid DM.
+ * Because the modifiers are appended at the end it's safe to use
+ * the final DM of the object without particles.
+ * However, when evaluating the DM all the particle modifiers must be valid,
+ * i.e. have the psys assigned already.
+ * To break this hen/egg problem we create all psys separately first (to collect required customdata masks),
+ * then create the DM, then add them to the object and make the psys modifiers ...
+ */
+ #define PSYS_FROM_FIRST (single_psys_from ? single_psys_from : ob_from->particlesystem.first)
+ #define PSYS_FROM_NEXT(cur) (single_psys_from ? NULL : (cur)->next)
+ totpsys = single_psys_from ? 1 : BLI_listbase_count(&ob_from->particlesystem);
+
+ tmp_psys = MEM_mallocN(sizeof(ParticleSystem*) * totpsys, "temporary particle system array");
+
+ cdmask = 0;
+ for (psys_from = PSYS_FROM_FIRST, i = 0;
+ psys_from;
+ psys_from = PSYS_FROM_NEXT(psys_from), ++i) {
+
+ psys = BKE_object_copy_particlesystem(psys_from);
+ tmp_psys[i] = psys;
+
+ if (psys_start == NULL)
+ psys_start = psys;
+
+ cdmask |= psys_emitter_customdata_mask(psys);
+ }
+ /* to iterate source and target psys in sync,
+ * we need to know where the newly added psys start
+ */
+ psys_start = totpsys > 0 ? tmp_psys[0] : NULL;
+
+ /* get the DM (psys and their modifiers have not been appended yet) */
+ final_dm = mesh_get_derived_final(scene, ob_to, cdmask);
+
+ /* now append psys to the object and make modifiers */
+ for (i = 0, psys_from = PSYS_FROM_FIRST;
+ i < totpsys;
+ ++i, psys_from = PSYS_FROM_NEXT(psys_from)) {
+
+ ParticleSystemModifierData *psmd;
+
+ psys = tmp_psys[i];
+
+ /* append to the object */
+ BLI_addtail(&ob_to->particlesystem, psys);
+
+ /* add a particle system modifier for each system */
+ md = modifier_new(eModifierType_ParticleSystem);
+ psmd = (ParticleSystemModifierData *)md;
+ /* push on top of the stack, no use trying to reproduce old stack order */
+ BLI_addtail(&ob_to->modifiers, md);
+
+ BLI_snprintf(md->name, sizeof(md->name), "ParticleSystem %i", i);
+ modifier_unique_name(&ob_to->modifiers, (ModifierData *)psmd);
+
+ psmd->psys = psys;
+ psmd->dm_final = CDDM_copy(final_dm);
+ CDDM_calc_normals(psmd->dm_final);
+ DM_ensure_tessface(psmd->dm_final);
+
+ if (psys_from->edit)
+ copy_particle_edit(scene, ob_to, psys, psys_from);
+
+ if (duplicate_settings) {
+ id_us_min(&psys->part->id);
+ psys->part = BKE_particlesettings_copy(bmain, psys->part);
+ }
+ }
+ MEM_freeN(tmp_psys);
+
+ /* note: do this after creating DM copies for all the particle system modifiers,
+ * the remapping otherwise makes final_dm invalid!
+ */
+ for (psys = psys_start, psys_from = PSYS_FROM_FIRST, i = 0;
+ psys;
+ psys = psys->next, psys_from = PSYS_FROM_NEXT(psys_from), ++i) {
+
+ float (*from_mat)[4], (*to_mat)[4];
+
+ switch (space) {
+ case PAR_COPY_SPACE_OBJECT:
+ from_mat = I;
+ to_mat = I;
+ break;
+ case PAR_COPY_SPACE_WORLD:
+ from_mat = ob_from->obmat;
+ to_mat = ob_to->obmat;
+ break;
+ default:
+ /* should not happen */
+ from_mat = to_mat = NULL;
+ BLI_assert(false);
+ break;
+ }
+ if (ob_from != ob_to) {
+ remap_hair_emitter(scene, ob_from, psys_from, ob_to, psys, psys->edit, from_mat, to_mat, psys_from->flag & PSYS_GLOBAL_HAIR, psys->flag & PSYS_GLOBAL_HAIR);
+ }
+
+ /* tag for recalc */
+// psys->recalc |= PSYS_RECALC_RESET;
+ }
+
+ #undef PSYS_FROM_FIRST
+ #undef PSYS_FROM_NEXT
+
+ DAG_id_tag_update(&ob_to->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to);
+ return true;
+}
+
+static int copy_particle_systems_poll(bContext *C)
+{
+ Object *ob;
+ if (!ED_operator_object_active_editable(C))
+ return false;
+
+ ob = ED_object_active_context(C);
+ if (BLI_listbase_is_empty(&ob->particlesystem))
+ return false;
+
+ return true;
+}
+
+static int copy_particle_systems_exec(bContext *C, wmOperator *op)
+{
+ const int space = RNA_enum_get(op->ptr, "space");
+ const bool remove_target_particles = RNA_boolean_get(op->ptr, "remove_target_particles");
+ const bool use_active = RNA_boolean_get(op->ptr, "use_active");
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ Object *ob_from = ED_object_active_context(C);
+ ParticleSystem *psys_from = use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data : NULL;
+
+ int changed_tot = 0;
+ int fail = 0;
+
+ CTX_DATA_BEGIN (C, Object *, ob_to, selected_editable_objects)
+ {
+ if (ob_from != ob_to) {
+ bool changed = false;
+ if (remove_target_particles) {
+ remove_particle_systems_from_object(ob_to);
+ changed = true;
+ }
+ if (copy_particle_systems_to_object(bmain, scene, ob_from, psys_from, ob_to, space, false))
+ changed = true;
+ else
+ fail++;
+
+ if (changed)
+ changed_tot++;
+ }
+ }
+ CTX_DATA_END;
+
+ if ((changed_tot == 0 && fail == 0) || fail) {
+ BKE_reportf(op->reports, RPT_ERROR,
+ "Copy particle systems to selected: %d done, %d failed",
+ changed_tot, fail);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot)
+{
+ static EnumPropertyItem space_items[] = {
+ {PAR_COPY_SPACE_OBJECT, "OBJECT", 0, "Object", "Copy inside each object's local space"},
+ {PAR_COPY_SPACE_WORLD, "WORLD", 0, "World", "Copy in world space"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ ot->name = "Copy Particle Systems";
+ ot->description = "Copy particle systems from the active object to selected objects";
+ ot->idname = "PARTICLE_OT_copy_particle_systems";
+
+ ot->poll = copy_particle_systems_poll;
+ ot->exec = copy_particle_systems_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "space", space_items, PAR_COPY_SPACE_OBJECT, "Space", "Space transform for copying from one object to another");
+ RNA_def_boolean(ot->srna, "remove_target_particles", true, "Remove Target Particles", "Remove particle systems on the target objects");
+ RNA_def_boolean(ot->srna, "use_active", false, "Use Active", "Use the active particle system from the context");
+}
+
+static int duplicate_particle_systems_poll(bContext *C)
+{
+ if (!ED_operator_object_active_editable(C)) {
+ return false;
+ }
+ Object *ob = ED_object_active_context(C);
+ if (BLI_listbase_is_empty(&ob->particlesystem)) {
+ return false;
+ }
+ return true;
+}
+
+static int duplicate_particle_systems_exec(bContext *C, wmOperator *op)
+{
+ const bool duplicate_settings = RNA_boolean_get(op->ptr, "use_duplicate_settings");
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = ED_object_active_context(C);
+ ParticleSystem *psys = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data;
+ copy_particle_systems_to_object(CTX_data_main(C), scene, ob, psys, ob,
+ PAR_COPY_SPACE_OBJECT, duplicate_settings);
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_duplicate_particle_system(wmOperatorType *ot)
+{
+ ot->name = "Duplicate Particle Systems";
+ ot->description = "Duplicate particle system within the active object";
+ ot->idname = "PARTICLE_OT_duplicate_particle_system";
+
+ ot->poll = duplicate_particle_systems_poll;
+ ot->exec = duplicate_particle_systems_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "use_duplicate_settings", false, "Duplicate Settings",
+ "Duplicate settings as well, so new particle system uses own settings");
+}
diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h
index a5b59feba6b..6b6df15e987 100644
--- a/source/blender/editors/physics/physics_intern.h
+++ b/source/blender/editors/physics/physics_intern.h
@@ -35,6 +35,64 @@
struct wmOperatorType;
+/* particle_edit.c */
+void PARTICLE_OT_select_all(struct wmOperatorType *ot);
+void PARTICLE_OT_select_roots(struct wmOperatorType *ot);
+void PARTICLE_OT_select_tips(struct wmOperatorType *ot);
+void PARTICLE_OT_select_random(struct wmOperatorType *ot);
+void PARTICLE_OT_select_linked(struct wmOperatorType *ot);
+void PARTICLE_OT_select_less(struct wmOperatorType *ot);
+void PARTICLE_OT_select_more(struct wmOperatorType *ot);
+
+void PARTICLE_OT_hide(struct wmOperatorType *ot);
+void PARTICLE_OT_reveal(struct wmOperatorType *ot);
+
+void PARTICLE_OT_rekey(struct wmOperatorType *ot);
+void PARTICLE_OT_subdivide(struct wmOperatorType *ot);
+void PARTICLE_OT_remove_doubles(struct wmOperatorType *ot);
+void PARTICLE_OT_weight_set(struct wmOperatorType *ot);
+void PARTICLE_OT_delete(struct wmOperatorType *ot);
+void PARTICLE_OT_mirror(struct wmOperatorType *ot);
+
+void PARTICLE_OT_brush_edit(struct wmOperatorType *ot);
+
+void PARTICLE_OT_shape_cut(struct wmOperatorType *ot);
+
+void PARTICLE_OT_particle_edit_toggle(struct wmOperatorType *ot);
+void PARTICLE_OT_edited_clear(struct wmOperatorType *ot);
+
+void PARTICLE_OT_unify_length(struct wmOperatorType *ot);
+
+/* particle_object.c */
+void OBJECT_OT_particle_system_add(struct wmOperatorType *ot);
+void OBJECT_OT_particle_system_remove(struct wmOperatorType *ot);
+
+void PARTICLE_OT_new(struct wmOperatorType *ot);
+void PARTICLE_OT_new_target(struct wmOperatorType *ot);
+void PARTICLE_OT_target_remove(struct wmOperatorType *ot);
+void PARTICLE_OT_target_move_up(struct wmOperatorType *ot);
+void PARTICLE_OT_target_move_down(struct wmOperatorType *ot);
+void PARTICLE_OT_connect_hair(struct wmOperatorType *ot);
+void PARTICLE_OT_disconnect_hair(struct wmOperatorType *ot);
+void PARTICLE_OT_copy_particle_systems(struct wmOperatorType *ot);
+void PARTICLE_OT_duplicate_particle_system(struct wmOperatorType *ot);
+
+void PARTICLE_OT_dupliob_copy(struct wmOperatorType *ot);
+void PARTICLE_OT_dupliob_remove(struct wmOperatorType *ot);
+void PARTICLE_OT_dupliob_move_up(struct wmOperatorType *ot);
+void PARTICLE_OT_dupliob_move_down(struct wmOperatorType *ot);
+
+/* particle_boids.c */
+void BOID_OT_rule_add(struct wmOperatorType *ot);
+void BOID_OT_rule_del(struct wmOperatorType *ot);
+void BOID_OT_rule_move_up(struct wmOperatorType *ot);
+void BOID_OT_rule_move_down(struct wmOperatorType *ot);
+
+void BOID_OT_state_add(struct wmOperatorType *ot);
+void BOID_OT_state_del(struct wmOperatorType *ot);
+void BOID_OT_state_move_up(struct wmOperatorType *ot);
+void BOID_OT_state_move_down(struct wmOperatorType *ot);
+
/* physics_fluid.c */
void FLUID_OT_bake(struct wmOperatorType *ot);
@@ -45,6 +103,15 @@ void DPAINT_OT_surface_slot_remove(struct wmOperatorType *ot);
void DPAINT_OT_type_toggle(struct wmOperatorType *ot);
void DPAINT_OT_output_toggle(struct wmOperatorType *ot);
+/* physics_pointcache.c */
+void PTCACHE_OT_bake_all(struct wmOperatorType *ot);
+void PTCACHE_OT_free_bake_all(struct wmOperatorType *ot);
+void PTCACHE_OT_bake(struct wmOperatorType *ot);
+void PTCACHE_OT_free_bake(struct wmOperatorType *ot);
+void PTCACHE_OT_bake_from_cache(struct wmOperatorType *ot);
+void PTCACHE_OT_add(struct wmOperatorType *ot);
+void PTCACHE_OT_remove(struct wmOperatorType *ot);
+
/* rigidbody_object.c */
void RIGIDBODY_OT_object_add(struct wmOperatorType *ot);
void RIGIDBODY_OT_object_remove(struct wmOperatorType *ot);
diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c
index d0cb7fd12a9..0c907f19753 100644
--- a/source/blender/editors/physics/physics_ops.c
+++ b/source/blender/editors/physics/physics_ops.c
@@ -42,8 +42,54 @@
/***************************** particles ***********************************/
-static void operatortypes_rigidbody(void)
+static void operatortypes_particle(void)
{
+ WM_operatortype_append(PARTICLE_OT_select_all);
+ WM_operatortype_append(PARTICLE_OT_select_roots);
+ WM_operatortype_append(PARTICLE_OT_select_tips);
+ WM_operatortype_append(PARTICLE_OT_select_random);
+ WM_operatortype_append(PARTICLE_OT_select_linked);
+ WM_operatortype_append(PARTICLE_OT_select_less);
+ WM_operatortype_append(PARTICLE_OT_select_more);
+
+ WM_operatortype_append(PARTICLE_OT_hide);
+ WM_operatortype_append(PARTICLE_OT_reveal);
+
+ WM_operatortype_append(PARTICLE_OT_rekey);
+ WM_operatortype_append(PARTICLE_OT_subdivide);
+ WM_operatortype_append(PARTICLE_OT_remove_doubles);
+ WM_operatortype_append(PARTICLE_OT_weight_set);
+ WM_operatortype_append(PARTICLE_OT_delete);
+ WM_operatortype_append(PARTICLE_OT_mirror);
+
+ WM_operatortype_append(PARTICLE_OT_brush_edit);
+
+ WM_operatortype_append(PARTICLE_OT_shape_cut);
+
+ WM_operatortype_append(PARTICLE_OT_particle_edit_toggle);
+ WM_operatortype_append(PARTICLE_OT_edited_clear);
+
+ WM_operatortype_append(PARTICLE_OT_unify_length);
+
+
+ WM_operatortype_append(OBJECT_OT_particle_system_add);
+ WM_operatortype_append(OBJECT_OT_particle_system_remove);
+
+ WM_operatortype_append(PARTICLE_OT_new);
+ WM_operatortype_append(PARTICLE_OT_new_target);
+ WM_operatortype_append(PARTICLE_OT_target_remove);
+ WM_operatortype_append(PARTICLE_OT_target_move_up);
+ WM_operatortype_append(PARTICLE_OT_target_move_down);
+ WM_operatortype_append(PARTICLE_OT_connect_hair);
+ WM_operatortype_append(PARTICLE_OT_disconnect_hair);
+ WM_operatortype_append(PARTICLE_OT_copy_particle_systems);
+ WM_operatortype_append(PARTICLE_OT_duplicate_particle_system);
+
+ WM_operatortype_append(PARTICLE_OT_dupliob_copy);
+ WM_operatortype_append(PARTICLE_OT_dupliob_remove);
+ WM_operatortype_append(PARTICLE_OT_dupliob_move_up);
+ WM_operatortype_append(PARTICLE_OT_dupliob_move_down);
+
WM_operatortype_append(RIGIDBODY_OT_object_add);
WM_operatortype_append(RIGIDBODY_OT_object_remove);
@@ -61,6 +107,79 @@ static void operatortypes_rigidbody(void)
// WM_operatortype_append(RIGIDBODY_OT_world_export);
}
+static void keymap_particle(wmKeyConfig *keyconf)
+{
+ wmKeyMapItem *kmi;
+ wmKeyMap *keymap;
+
+ keymap = WM_keymap_find(keyconf, "Particle", 0, 0);
+ keymap->poll = PE_poll;
+
+ kmi = WM_keymap_add_item(keymap, "PARTICLE_OT_select_all", AKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
+ kmi = WM_keymap_add_item(keymap, "PARTICLE_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
+
+ WM_keymap_add_item(keymap, "PARTICLE_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "PARTICLE_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0);
+
+ kmi = WM_keymap_add_item(keymap, "PARTICLE_OT_select_linked", LKEY, KM_PRESS, 0, 0);
+ RNA_boolean_set(kmi->ptr, "deselect", false);
+ kmi = WM_keymap_add_item(keymap, "PARTICLE_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "deselect", true);
+
+ WM_keymap_add_item(keymap, "PARTICLE_OT_delete", XKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "PARTICLE_OT_delete", DELKEY, KM_PRESS, 0, 0);
+
+ WM_keymap_add_item(keymap, "PARTICLE_OT_reveal", HKEY, KM_PRESS, KM_ALT, 0);
+ kmi = WM_keymap_add_item(keymap, "PARTICLE_OT_hide", HKEY, KM_PRESS, 0, 0);
+ RNA_boolean_set(kmi->ptr, "unselected", false);
+ kmi = WM_keymap_add_item(keymap, "PARTICLE_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "unselected", true);
+
+ /* Shift+LMB behavior first, so it has priority over KM_ANY item below. */
+ kmi = WM_keymap_verify_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "release_confirm", true);
+ RNA_boolean_set(kmi->ptr, "use_planar_constraint", true);
+ /* Using KM_ANY here to allow holding modifiers before starting to transform. */
+ kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
+ RNA_boolean_set(kmi->ptr, "release_confirm", true);
+ RNA_boolean_set(kmi->ptr, "use_planar_constraint", false);
+
+ WM_keymap_add_item(keymap, "PARTICLE_OT_brush_edit", LEFTMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "PARTICLE_OT_brush_edit", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
+
+ /* size radial control */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.particle_edit.brush.size");
+
+ /* size radial control */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.particle_edit.brush.strength");
+
+ WM_keymap_add_menu(keymap, "VIEW3D_MT_particle_specials", WKEY, KM_PRESS, 0, 0);
+
+ WM_keymap_add_item(keymap, "PARTICLE_OT_weight_set", KKEY, KM_PRESS, KM_SHIFT, 0);
+
+ ED_keymap_proportional_cycle(keyconf, keymap);
+ ED_keymap_proportional_editmode(keyconf, keymap, false);
+}
+
+/******************************* boids *************************************/
+
+static void operatortypes_boids(void)
+{
+ WM_operatortype_append(BOID_OT_rule_add);
+ WM_operatortype_append(BOID_OT_rule_del);
+ WM_operatortype_append(BOID_OT_rule_move_up);
+ WM_operatortype_append(BOID_OT_rule_move_down);
+
+ WM_operatortype_append(BOID_OT_state_add);
+ WM_operatortype_append(BOID_OT_state_del);
+ WM_operatortype_append(BOID_OT_state_move_up);
+ WM_operatortype_append(BOID_OT_state_move_down);
+}
+
/********************************* fluid ***********************************/
static void operatortypes_fluid(void)
@@ -68,6 +187,19 @@ static void operatortypes_fluid(void)
WM_operatortype_append(FLUID_OT_bake);
}
+/**************************** point cache **********************************/
+
+static void operatortypes_pointcache(void)
+{
+ WM_operatortype_append(PTCACHE_OT_bake_all);
+ WM_operatortype_append(PTCACHE_OT_free_bake_all);
+ WM_operatortype_append(PTCACHE_OT_bake);
+ WM_operatortype_append(PTCACHE_OT_free_bake);
+ WM_operatortype_append(PTCACHE_OT_bake_from_cache);
+ WM_operatortype_append(PTCACHE_OT_add);
+ WM_operatortype_append(PTCACHE_OT_remove);
+}
+
/********************************* dynamic paint ***********************************/
static void operatortypes_dynamicpaint(void)
@@ -79,17 +211,31 @@ static void operatortypes_dynamicpaint(void)
WM_operatortype_append(DPAINT_OT_output_toggle);
}
+//static void keymap_pointcache(wmWindowManager *wm)
+//{
+// wmKeyMap *keymap = WM_keymap_find(wm, "Pointcache", 0, 0);
+//
+// WM_keymap_add_item(keymap, "PHYSICS_OT_bake_all", AKEY, KM_PRESS, 0, 0);
+// WM_keymap_add_item(keymap, "PHYSICS_OT_free_all", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
+// WM_keymap_add_item(keymap, "PHYSICS_OT_bake_particle_system", PADMINUS, KM_PRESS, KM_CTRL, 0);
+// WM_keymap_add_item(keymap, "PHYSICS_OT_free_particle_system", LKEY, KM_PRESS, 0, 0);
+//}
+
/****************************** general ************************************/
void ED_operatortypes_physics(void)
{
- operatortypes_rigidbody();
+ operatortypes_particle();
+ operatortypes_boids();
operatortypes_fluid();
+ operatortypes_pointcache();
operatortypes_dynamicpaint();
}
-void ED_keymap_physics(wmKeyConfig *UNUSED(keyconf))
+void ED_keymap_physics(wmKeyConfig *keyconf)
{
+ keymap_particle(keyconf);
+ //keymap_pointcache(keyconf);
}
diff --git a/source/blender/editors/physics/physics_pointcache.c b/source/blender/editors/physics/physics_pointcache.c
new file mode 100644
index 00000000000..e81aa584586
--- /dev/null
+++ b/source/blender/editors/physics/physics_pointcache.c
@@ -0,0 +1,469 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/physics/physics_pointcache.c
+ * \ingroup edphys
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_scene_types.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+
+#include "ED_particle.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "physics_intern.h"
+
+static int ptcache_bake_all_poll(bContext *C)
+{
+ return CTX_data_scene(C) != NULL;
+}
+
+static int ptcache_poll(bContext *C)
+{
+ PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
+ return (ptr.data && ptr.id.data);
+}
+
+typedef struct PointCacheJob {
+ void *owner;
+ short *stop, *do_update;
+ float *progress;
+
+ PTCacheBaker *baker;
+} PointCacheJob;
+
+static void ptcache_job_free(void *customdata)
+{
+ PointCacheJob *job = customdata;
+ MEM_freeN(job->baker);
+ MEM_freeN(job);
+}
+
+static int ptcache_job_break(void *customdata)
+{
+ PointCacheJob *job = customdata;
+
+ if (G.is_break) {
+ return 1;
+ }
+
+ if (job->stop && *(job->stop)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void ptcache_job_update(void *customdata, float progress, int *cancel)
+{
+ PointCacheJob *job = customdata;
+
+ if (ptcache_job_break(job)) {
+ *cancel = 1;
+ }
+
+ *(job->do_update) = true;
+ *(job->progress) = progress;
+}
+
+static void ptcache_job_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ PointCacheJob *job = customdata;
+
+ job->stop = stop;
+ job->do_update = do_update;
+ job->progress = progress;
+
+ G.is_break = false;
+
+ /* XXX annoying hack: needed to prevent data corruption when changing
+ * scene frame in separate threads
+ */
+ G.is_rendering = true;
+ BKE_spacedata_draw_locks(true);
+
+ BKE_ptcache_bake(job->baker);
+
+ *do_update = true;
+ *stop = 0;
+}
+
+static void ptcache_job_endjob(void *customdata)
+{
+ PointCacheJob *job = customdata;
+ Scene *scene = job->baker->scene;
+
+ G.is_rendering = false;
+ BKE_spacedata_draw_locks(false);
+
+ WM_set_locked_interface(G.main->wm.first, false);
+
+ WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
+ WM_main_add_notifier(NC_OBJECT | ND_POINTCACHE, job->baker->pid.ob);
+}
+
+static void ptcache_free_bake(PointCache *cache)
+{
+ if (cache->edit) {
+ if (!cache->edit->edited || 1) {// XXX okee("Lose changes done in particle mode?")) {
+ PE_free_ptcache_edit(cache->edit);
+ cache->edit = NULL;
+ cache->flag &= ~PTCACHE_BAKED;
+ }
+ }
+ else {
+ cache->flag &= ~PTCACHE_BAKED;
+ }
+}
+
+static PTCacheBaker *ptcache_baker_create(bContext *C, wmOperator *op, bool all)
+{
+ PTCacheBaker *baker = MEM_callocN(sizeof(PTCacheBaker), "PTCacheBaker");
+
+ baker->main = CTX_data_main(C);
+ baker->scene = CTX_data_scene(C);
+ baker->bake = RNA_boolean_get(op->ptr, "bake");
+ baker->render = 0;
+ baker->anim_init = 0;
+ baker->quick_step = 1;
+
+ if (!all) {
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
+ Object *ob = ptr.id.data;
+ PointCache *cache = ptr.data;
+
+ ListBase pidlist;
+ BKE_ptcache_ids_from_object(&pidlist, ob, baker->scene, MAX_DUPLI_RECUR);
+
+ for (PTCacheID *pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache) {
+ baker->pid = *pid;
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+ }
+
+ return baker;
+}
+
+static int ptcache_bake_exec(bContext *C, wmOperator *op)
+{
+ bool all = STREQ(op->type->idname, "PTCACHE_OT_bake_all");
+
+ PTCacheBaker *baker = ptcache_baker_create(C, op, all);
+ BKE_ptcache_bake(baker);
+ MEM_freeN(baker);
+
+ return OPERATOR_FINISHED;
+}
+
+static int ptcache_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ bool all = STREQ(op->type->idname, "PTCACHE_OT_bake_all");
+
+ PointCacheJob *job = MEM_mallocN(sizeof(PointCacheJob), "PointCacheJob");
+ job->baker = ptcache_baker_create(C, op, all);
+ job->baker->bake_job = job;
+ job->baker->update_progress = ptcache_job_update;
+
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_data_scene(C),
+ "Point Cache", WM_JOB_PROGRESS, WM_JOB_TYPE_POINTCACHE);
+
+ WM_jobs_customdata_set(wm_job, job, ptcache_job_free);
+ WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_POINTCACHE, NC_OBJECT | ND_POINTCACHE);
+ WM_jobs_callbacks(wm_job, ptcache_job_startjob, NULL, NULL, ptcache_job_endjob);
+
+ WM_set_locked_interface(CTX_wm_manager(C), true);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+
+ WM_event_add_modal_handler(C, op);
+
+ /* we must run modal until the bake job is done, otherwise the undo push
+ * happens before the job ends, which can lead to race conditions between
+ * the baking and file writing code */
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int ptcache_bake_modal(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Scene *scene = (Scene *) op->customdata;
+
+ /* no running blender, remove handler and pass through */
+ if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_POINTCACHE)) {
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+static void ptcache_bake_cancel(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ Scene *scene = (Scene *) op->customdata;
+
+ /* kill on cancel, because job is using op->reports */
+ WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_POINTCACHE);
+}
+
+static int ptcache_free_bake_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene= CTX_data_scene(C);
+ Base *base;
+ PTCacheID *pid;
+ ListBase pidlist;
+
+ for (base=scene->base.first; base; base= base->next) {
+ BKE_ptcache_ids_from_object(&pidlist, base->object, scene, MAX_DUPLI_RECUR);
+
+ for (pid=pidlist.first; pid; pid=pid->next) {
+ ptcache_free_bake(pid->cache);
+ }
+
+ BLI_freelistN(&pidlist);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, base->object);
+ }
+
+ WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void PTCACHE_OT_bake_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Bake All Physics";
+ ot->description = "Bake all physics";
+ ot->idname = "PTCACHE_OT_bake_all";
+
+ /* api callbacks */
+ ot->exec = ptcache_bake_exec;
+ ot->invoke = ptcache_bake_invoke;
+ ot->modal = ptcache_bake_modal;
+ ot->cancel = ptcache_bake_cancel;
+ ot->poll = ptcache_bake_all_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "bake", 1, "Bake", "");
+}
+void PTCACHE_OT_free_bake_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Free All Physics Bakes";
+ ot->idname = "PTCACHE_OT_free_bake_all";
+ ot->description = "Free all baked caches of all objects in the current scene";
+
+ /* api callbacks */
+ ot->exec = ptcache_free_bake_all_exec;
+ ot->poll = ptcache_bake_all_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
+ PointCache *cache= ptr.data;
+ Object *ob= ptr.id.data;
+
+ ptcache_free_bake(cache);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+static int ptcache_bake_from_cache_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
+ PointCache *cache= ptr.data;
+ Object *ob= ptr.id.data;
+
+ cache->flag |= PTCACHE_BAKED;
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+void PTCACHE_OT_bake(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Bake Physics";
+ ot->description = "Bake physics";
+ ot->idname = "PTCACHE_OT_bake";
+
+ /* api callbacks */
+ ot->exec = ptcache_bake_exec;
+ ot->invoke = ptcache_bake_invoke;
+ ot->modal = ptcache_bake_modal;
+ ot->cancel = ptcache_bake_cancel;
+ ot->poll = ptcache_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "bake", 0, "Bake", "");
+}
+void PTCACHE_OT_free_bake(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Free Physics Bake";
+ ot->description = "Free physics bake";
+ ot->idname = "PTCACHE_OT_free_bake";
+
+ /* api callbacks */
+ ot->exec = ptcache_free_bake_exec;
+ ot->poll = ptcache_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+void PTCACHE_OT_bake_from_cache(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Bake From Cache";
+ ot->description = "Bake from cache";
+ ot->idname = "PTCACHE_OT_bake_from_cache";
+
+ /* api callbacks */
+ ot->exec = ptcache_bake_from_cache_exec;
+ ot->poll = ptcache_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int ptcache_add_new_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
+ Object *ob= ptr.id.data;
+ PointCache *cache= ptr.data;
+ PTCacheID *pid;
+ ListBase pidlist;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, scene, MAX_DUPLI_RECUR);
+
+ for (pid=pidlist.first; pid; pid=pid->next) {
+ if (pid->cache == cache) {
+ PointCache *cache_new = BKE_ptcache_add(pid->ptcaches);
+ cache_new->step = pid->default_step;
+ *(pid->cache_ptr) = cache_new;
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+
+ WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+static int ptcache_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA ptr= CTX_data_pointer_get_type(C, "point_cache", &RNA_PointCache);
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ptr.id.data;
+ PointCache *cache= ptr.data;
+ PTCacheID *pid;
+ ListBase pidlist;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, scene, MAX_DUPLI_RECUR);
+
+ for (pid=pidlist.first; pid; pid=pid->next) {
+ if (pid->cache == cache) {
+ if (pid->ptcaches->first == pid->ptcaches->last)
+ continue; /* don't delete last cache */
+
+ BLI_remlink(pid->ptcaches, pid->cache);
+ BKE_ptcache_free(pid->cache);
+ *(pid->cache_ptr) = pid->ptcaches->first;
+
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+void PTCACHE_OT_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add New Cache";
+ ot->description = "Add new cache";
+ ot->idname = "PTCACHE_OT_add";
+
+ /* api callbacks */
+ ot->exec = ptcache_add_new_exec;
+ ot->poll = ptcache_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+void PTCACHE_OT_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Delete Current Cache";
+ ot->description = "Delete current cache";
+ ot->idname = "PTCACHE_OT_remove";
+
+ /* api callbacks */
+ ot->exec = ptcache_remove_exec;
+ ot->poll = ptcache_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 7eb2552487b..837573ad175 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -36,6 +36,7 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "DNA_world_types.h"
@@ -1774,6 +1775,9 @@ static void copy_mtex_copybuf(ID *id)
mtex = &(((World *)id)->mtex[(int)((World *)id)->texact]);
// mtex= wrld->mtex[(int)wrld->texact]; // TODO
break;
+ case ID_PA:
+ mtex = &(((ParticleSettings *)id)->mtex[(int)((ParticleSettings *)id)->texact]);
+ break;
case ID_LS:
mtex = &(((FreestyleLineStyle *)id)->mtex[(int)((FreestyleLineStyle *)id)->texact]);
break;
@@ -1807,6 +1811,9 @@ static void paste_mtex_copybuf(ID *id)
mtex = &(((World *)id)->mtex[(int)((World *)id)->texact]);
// mtex= wrld->mtex[(int)wrld->texact]; // TODO
break;
+ case ID_PA:
+ mtex = &(((ParticleSettings *)id)->mtex[(int)((ParticleSettings *)id)->texact]);
+ break;
case ID_LS:
mtex = &(((FreestyleLineStyle *)id)->mtex[(int)((FreestyleLineStyle *)id)->texact]);
break;
@@ -1875,6 +1882,7 @@ static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op))
Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data;
Lamp *la = CTX_data_pointer_get_type(C, "lamp", &RNA_Lamp).data;
World *wo = CTX_data_pointer_get_type(C, "world", &RNA_World).data;
+ ParticleSystem *psys = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data;
FreestyleLineStyle *linestyle = CTX_data_pointer_get_type(C, "line_style", &RNA_FreestyleLineStyle).data;
if (ma)
@@ -1883,6 +1891,8 @@ static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op))
id = &la->id;
else if (wo)
id = &wo->id;
+ else if (psys)
+ id = &psys->part->id;
else if (linestyle)
id = &linestyle->id;
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 2cf0a16f236..c165bbfd301 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -392,6 +392,12 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
+ else if (CTX_data_equals(member, "particle_edit_object")) {
+ if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT))
+ CTX_data_id_pointer_set(result, &obact->id);
+
+ return 1;
+ }
else if (CTX_data_equals(member, "sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index ef99fedbec0..991025a4d5d 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -43,6 +43,7 @@
#include "DNA_armature_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_brush_types.h"
#include "DNA_object_types.h"
@@ -2421,6 +2422,21 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
/* frees prev buffer */
copy_wpaint_prev(ts->wpaint, NULL, 0);
+
+ /* and particles too */
+ if (ob->particlesystem.first) {
+ ParticleSystem *psys;
+ int i;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ for (i = 0; i < PSYS_TOT_VG; i++) {
+ if (psys->vgroup[i] == ob->actdef) {
+ psys->recalc |= PSYS_RECALC_RESET;
+ break;
+ }
+ }
+ }
+ }
DAG_id_tag_update(ob->data, 0);
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index a1ecb1c4f5c..da3364d872d 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -46,13 +46,13 @@
#include "DNA_world_types.h"
#include "DNA_brush_types.h"
#include "DNA_linestyle_types.h"
-#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_action.h"
#include "BKE_material.h"
#include "BKE_modifier.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
#include "BKE_screen.h"
#include "BKE_texture.h"
#include "BKE_linestyle.h"
@@ -339,6 +339,34 @@ static int buttons_context_path_pose_bone(ButsContextPath *path)
return 0;
}
+
+static int buttons_context_path_particle(ButsContextPath *path)
+{
+ Object *ob;
+ ParticleSystem *psys;
+ PointerRNA *ptr = &path->ptr[path->len - 1];
+
+ /* if we already have (pinned) particle settings, we're done */
+ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSettings)) {
+ return 1;
+ }
+ /* if we have an object, get the active particle system */
+ if (buttons_context_path_object(path)) {
+ ob = path->ptr[path->len - 1].data;
+
+ if (ob && ob->type == OB_MESH) {
+ psys = psys_get_current(ob);
+
+ RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]);
+ path->len++;
+ return 1;
+ }
+ }
+
+ /* no path to a particle system possible */
+ return 0;
+}
+
static int buttons_context_path_brush(ButsContextPath *path)
{
Scene *scene;
@@ -393,6 +421,8 @@ static int buttons_context_path_texture(ButsContextPath *path, ButsContextTextur
buttons_context_path_world(path);
else if (GS(id->name) == ID_LA)
buttons_context_path_data(path, OB_LAMP);
+ else if (GS(id->name) == ID_PA)
+ buttons_context_path_particle(path);
else if (GS(id->name) == ID_OB)
buttons_context_path_object(path);
else if (GS(id->name) == ID_LS)
@@ -411,6 +441,7 @@ static int buttons_context_path_texture(ButsContextPath *path, ButsContextTextur
Material *ma;
Lamp *la;
World *wo;
+ ParticleSystem *psys;
FreestyleLineStyle *ls;
Tex *tex;
PointerRNA *ptr = &path->ptr[path->len - 1];
@@ -431,6 +462,28 @@ static int buttons_context_path_texture(ButsContextPath *path, ButsContextTextur
return 1;
}
}
+ /* try particles */
+ else if ((path->tex_ctx == SB_TEXC_PARTICLES) && buttons_context_path_particle(path)) {
+ if (path->ptr[path->len - 1].type == &RNA_ParticleSettings) {
+ ParticleSettings *part = path->ptr[path->len - 1].data;
+
+ tex = give_current_particle_texture(part);
+ RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
+ path->len++;
+ return 1;
+ }
+ else {
+ psys = path->ptr[path->len - 1].data;
+
+ if (psys && psys->part && GS(psys->part->id.name) == ID_PA) {
+ tex = give_current_particle_texture(psys->part);
+
+ RNA_id_pointer_create(&tex->id, &path->ptr[path->len]);
+ path->len++;
+ return 1;
+ }
+ }
+ }
/* try material */
else if ((path->tex_ctx == SB_TEXC_MATERIAL) && buttons_context_path_material(path, true, false)) {
ma = path->ptr[path->len - 1].data;
@@ -557,6 +610,9 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma
case BCONTEXT_DATA:
found = buttons_context_path_data(path, -1);
break;
+ case BCONTEXT_PARTICLE:
+ found = buttons_context_path_particle(path);
+ break;
case BCONTEXT_MATERIAL:
found = buttons_context_path_material(path, false, (sbuts->texuser != NULL));
break;
@@ -844,7 +900,14 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
ButsContextTexture *ct = sbuts->texuser;
PointerRNA *ptr;
- if (ct) {
+ /* Particles slots are used in both old and new textures handling. */
+ if ((ptr = get_pointer_type(path, &RNA_ParticleSystem))) {
+ ParticleSettings *part = ((ParticleSystem *)ptr->data)->part;
+
+ if (part)
+ CTX_data_pointer_set(result, &part->id, &RNA_ParticleSettingsTextureSlot, part->mtex[(int)part->texact]);
+ }
+ else if (ct) {
return 0; /* new shading system */
}
else if ((ptr = get_pointer_type(path, &RNA_Material))) {
@@ -900,6 +963,38 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
set_pointer_type(path, result, &RNA_PoseBone);
return 1;
}
+ else if (CTX_data_equals(member, "particle_system")) {
+ set_pointer_type(path, result, &RNA_ParticleSystem);
+ return 1;
+ }
+ else if (CTX_data_equals(member, "particle_system_editable")) {
+ if (PE_poll((bContext *)C))
+ set_pointer_type(path, result, &RNA_ParticleSystem);
+ else
+ CTX_data_pointer_set(result, NULL, &RNA_ParticleSystem, NULL);
+ return 1;
+ }
+ else if (CTX_data_equals(member, "particle_settings")) {
+ /* only available when pinned */
+ PointerRNA *ptr = get_pointer_type(path, &RNA_ParticleSettings);
+
+ if (ptr && ptr->data) {
+ CTX_data_pointer_set(result, ptr->id.data, &RNA_ParticleSettings, ptr->data);
+ return 1;
+ }
+ else {
+ /* get settings from active particle system instead */
+ ptr = get_pointer_type(path, &RNA_ParticleSystem);
+
+ if (ptr && ptr->data) {
+ ParticleSettings *part = ((ParticleSystem *)ptr->data)->part;
+ CTX_data_pointer_set(result, ptr->id.data, &RNA_ParticleSettings, part);
+ return 1;
+ }
+ }
+ set_pointer_type(path, result, &RNA_ParticleSettings);
+ return 1;
+ }
else if (CTX_data_equals(member, "cloth")) {
PointerRNA *ptr = get_pointer_type(path, &RNA_Object);
@@ -1069,6 +1164,14 @@ ID *buttons_context_id_path(const bContext *C)
for (a = path->len - 1; a >= 0; a--) {
ptr = &path->ptr[a];
+ /* pin particle settings instead of system, since only settings are an idblock*/
+ if (sbuts->mainb == BCONTEXT_PARTICLE && sbuts->flag & SB_PIN_CONTEXT) {
+ if (ptr->type == &RNA_ParticleSystem && ptr->data) {
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ return &psys->part->id;
+ }
+ }
+
if (ptr->id.data) {
return ptr->id.data;
}
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 87cf0e8c9e3..72de7e5c81c 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -46,6 +46,7 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -58,6 +59,7 @@
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#ifdef WITH_FREESTYLE
# include "BKE_freestyle.h"
@@ -96,6 +98,12 @@ bool ED_texture_context_check_lamp(const bContext *C)
return (ob && (ob->type == OB_LAMP));
}
+bool ED_texture_context_check_particles(const bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ return (ob && ob->particlesystem.first);
+}
+
bool ED_texture_context_check_linestyle(const bContext *C)
{
#ifdef WITH_FREESTYLE
@@ -170,6 +178,7 @@ static void set_texture_context(const bContext *C, SpaceButs *sbuts)
bool valid_world = ED_texture_context_check_world(C);
bool valid_material = ED_texture_context_check_material(C);
bool valid_lamp = ED_texture_context_check_lamp(C);
+ bool valid_particles = ED_texture_context_check_particles(C);
bool valid_linestyle = ED_texture_context_check_linestyle(C);
bool valid_others = ED_texture_context_check_others(C);
@@ -183,6 +192,9 @@ static void set_texture_context(const bContext *C, SpaceButs *sbuts)
else if ((sbuts->mainb == BCONTEXT_DATA) && valid_lamp) {
sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_LAMP;
}
+ else if ((sbuts->mainb == BCONTEXT_PARTICLE) && valid_particles) {
+ sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_PARTICLES;
+ }
else if ((sbuts->mainb == BCONTEXT_RENDER_LAYER) && valid_linestyle) {
sbuts->texture_context = sbuts->texture_context_prev = SB_TEXC_LINESTYLE;
}
@@ -194,6 +206,7 @@ static void set_texture_context(const bContext *C, SpaceButs *sbuts)
(((sbuts->texture_context_prev == SB_TEXC_WORLD) && valid_world) ||
((sbuts->texture_context_prev == SB_TEXC_MATERIAL) && valid_material) ||
((sbuts->texture_context_prev == SB_TEXC_LAMP) && valid_lamp) ||
+ ((sbuts->texture_context_prev == SB_TEXC_PARTICLES) && valid_particles) ||
((sbuts->texture_context_prev == SB_TEXC_LINESTYLE) && valid_linestyle) ||
((sbuts->texture_context_prev == SB_TEXC_OTHER) && valid_others)))
{
@@ -203,6 +216,7 @@ static void set_texture_context(const bContext *C, SpaceButs *sbuts)
else if (((sbuts->texture_context == SB_TEXC_WORLD) && !valid_world) ||
((sbuts->texture_context == SB_TEXC_MATERIAL) && !valid_material) ||
((sbuts->texture_context == SB_TEXC_LAMP) && !valid_lamp) ||
+ ((sbuts->texture_context == SB_TEXC_PARTICLES) && !valid_particles) ||
((sbuts->texture_context == SB_TEXC_LINESTYLE) && !valid_linestyle) ||
((sbuts->texture_context == SB_TEXC_OTHER) && !valid_others))
{
@@ -214,6 +228,9 @@ static void set_texture_context(const bContext *C, SpaceButs *sbuts)
else if (valid_lamp) {
sbuts->texture_context = SB_TEXC_LAMP;
}
+ else if (valid_particles) {
+ sbuts->texture_context = SB_TEXC_PARTICLES;
+ }
else if (valid_linestyle) {
sbuts->texture_context = SB_TEXC_LINESTYLE;
}
@@ -358,9 +375,31 @@ static void buttons_texture_users_from_context(ListBase *users, const bContext *
buttons_texture_users_find_nodetree(users, &linestyle->id, linestyle->nodetree, N_("Line Style"));
if (ob) {
+ ParticleSystem *psys = psys_get_current(ob);
+ MTex *mtex;
+ int a;
+
/* modifiers */
modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users);
+ /* particle systems */
+ if (psys && !limited_mode) {
+ for (a = 0; a < MAX_MTEX; a++) {
+ mtex = psys->part->mtex[a];
+
+ if (mtex) {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ RNA_pointer_create(&psys->part->id, &RNA_ParticleSettingsTextureSlot, mtex, &ptr);
+ prop = RNA_struct_find_property(&ptr, "texture");
+
+ buttons_texture_user_property_add(users, &psys->part->id, ptr, prop, N_("Particles"),
+ RNA_struct_ui_icon(&RNA_ParticleSettings), psys->name);
+ }
+ }
+ }
+
/* field */
if (ob->pd && ob->pd->forcefield == PFIELD_TEXTURE) {
PointerRNA ptr;
@@ -490,6 +529,17 @@ static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg)
ct->texture = tex;
+ if (user->ptr.type == &RNA_ParticleSettingsTextureSlot) {
+ /* stupid exception for particle systems which still uses influence
+ * from the old texture system, set the active texture slots as well */
+ ParticleSettings *part = user->ptr.id.data;
+ int a;
+
+ for (a = 0; a < MAX_MTEX; a++)
+ if (user->ptr.data == part->mtex[a])
+ part->texact = a;
+ }
+
if (sbuts && tex)
sbuts->preview = 1;
}
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index f91a357504d..e4c23ad74f8 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -159,6 +159,8 @@ static void buttons_main_region_draw(const bContext *C, ARegion *ar)
ED_region_panels(C, ar, "material", sbuts->mainb, vertical);
else if (sbuts->mainb == BCONTEXT_TEXTURE)
ED_region_panels(C, ar, "texture", sbuts->mainb, vertical);
+ else if (sbuts->mainb == BCONTEXT_PARTICLE)
+ ED_region_panels(C, ar, "particle", sbuts->mainb, vertical);
else if (sbuts->mainb == BCONTEXT_PHYSICS)
ED_region_panels(C, ar, "physics", sbuts->mainb, vertical);
else if (sbuts->mainb == BCONTEXT_BONE)
@@ -279,6 +281,11 @@ static void buttons_area_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *
buttons_area_redraw(sa, BCONTEXT_CONSTRAINT);
buttons_area_redraw(sa, BCONTEXT_BONE_CONSTRAINT);
break;
+ case ND_PARTICLE:
+ if (wmn->action == NA_EDITED)
+ buttons_area_redraw(sa, BCONTEXT_PARTICLE);
+ sbuts->preview = 1;
+ break;
case ND_DRAW:
buttons_area_redraw(sa, BCONTEXT_OBJECT);
buttons_area_redraw(sa, BCONTEXT_DATA);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 5eb261890b2..7abe5ff5070 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -214,7 +214,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->filter_id = FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD |
FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA |
FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB |
- FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO |
+ FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO |
FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF;
if (U.uiflag & USER_HIDE_DOT) {
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index 6e156750815..8dc6c4229b2 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -35,7 +35,6 @@
#include "DNA_group_types.h"
#include "DNA_lattice_types.h"
#include "DNA_meta_types.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_math.h"
@@ -51,6 +50,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_key.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
#include "BKE_editmesh.h"
#include "ED_info.h"
@@ -273,7 +273,37 @@ static void stats_dupli_object(Base *base, Object *ob, SceneStats *stats)
{
if (base->flag & SELECT) stats->totobjsel++;
- if (ob->parent && (ob->parent->transflag & (OB_DUPLIVERTS | OB_DUPLIFACES))) {
+ if (ob->transflag & OB_DUPLIPARTS) {
+ /* Dupli Particles */
+ ParticleSystem *psys;
+ ParticleSettings *part;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ part = psys->part;
+
+ if (part->draw_as == PART_DRAW_OB && part->dup_ob) {
+ int tot = count_particles(psys);
+ stats_object(part->dup_ob, 0, tot, stats);
+ }
+ else if (part->draw_as == PART_DRAW_GR && part->dup_group) {
+ GroupObject *go;
+ int tot, totgroup = 0, cur = 0;
+
+ for (go = part->dup_group->gobject.first; go; go = go->next)
+ totgroup++;
+
+ for (go = part->dup_group->gobject.first; go; go = go->next) {
+ tot = count_particles_mod(psys, totgroup, cur);
+ stats_object(go->ob, 0, tot, stats);
+ cur++;
+ }
+ }
+ }
+
+ stats_object(ob, base->flag & SELECT, 1, stats);
+ stats->totobj++;
+ }
+ else if (ob->parent && (ob->parent->transflag & (OB_DUPLIVERTS | OB_DUPLIFACES))) {
/* Dupli Verts/Faces */
int tot;
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index bb6cf568425..3243579f7d0 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -136,6 +136,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
case ANIMTYPE_DSNTREE:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index 9a8a5df78e4..e9c46e9d04b 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -175,6 +175,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
case ANIMTYPE_DSNTREE:
+ case ANIMTYPE_DSPART:
case ANIMTYPE_DSMBALL:
case ANIMTYPE_DSARM:
case ANIMTYPE_DSMESH:
@@ -214,7 +215,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe
/* offset for start of channel (on LHS of channel-list) */
if (ale->id) {
/* special exception for materials and particles */
- if (GS(ale->id->name) == ID_MA)
+ if (ELEM(GS(ale->id->name), ID_MA, ID_PA))
offset = 21 + NLACHANNEL_BUTTON_WIDTH;
else
offset = 14;
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index fd55f4d4fdc..18f4a02ab72 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -1080,6 +1080,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
UI_icon_draw(x, y, ICON_MODIFIER); break;
case TSE_LINKED_OB:
UI_icon_draw(x, y, ICON_OBJECT_DATA); break;
+ case TSE_LINKED_PSYS:
+ UI_icon_draw(x, y, ICON_PARTICLES); break;
case TSE_MODIFIER:
{
Object *ob = (Object *)tselem->id;
@@ -1107,6 +1109,10 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
UI_icon_draw(x, y, ICON_MOD_SOFT); break;
case eModifierType_Boolean:
UI_icon_draw(x, y, ICON_MOD_BOOLEAN); break;
+ case eModifierType_ParticleSystem:
+ UI_icon_draw(x, y, ICON_MOD_PARTICLES); break;
+ case eModifierType_ParticleInstance:
+ UI_icon_draw(x, y, ICON_MOD_PARTICLES); break;
case eModifierType_EdgeSplit:
UI_icon_draw(x, y, ICON_MOD_EDGESPLIT); break;
case eModifierType_Array:
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index c5dfbf1819b..f23c294c488 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -61,7 +61,7 @@ typedef struct TreeElement {
#define TREESTORE_ID_TYPE(_id) \
(ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \
- ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_GD, ID_LS) || \
+ ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \
ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL)) /* Only in 'blendfile' mode ... :/ */
/* TreeElement->flag */
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index a9f834c509f..a73e160f357 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -617,6 +617,20 @@ static eOLDrawState tree_element_active_modifier(
return OL_DRAWSEL_NONE;
}
+static eOLDrawState tree_element_active_psys(
+ bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
+{
+ if (set != OL_SETSEL_NONE) {
+ Object *ob = (Object *)tselem->id;
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
+
+// XXX extern_set_butspace(F7KEY, 0);
+ }
+
+ return OL_DRAWSEL_NONE;
+}
+
static int tree_element_active_constraint(
bContext *C, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set)
{
@@ -795,6 +809,8 @@ eOLDrawState tree_element_type_active(
return OL_DRAWSEL_NORMAL;
}
break;
+ case TSE_LINKED_PSYS:
+ return tree_element_active_psys(C, scene, te, tselem, set);
case TSE_POSE_BASE:
return tree_element_active_pose(C, scene, te, tselem, set);
case TSE_POSE_CHANNEL:
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index d1c75b01157..ec46c5df9a0 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -46,6 +46,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_world_types.h"
#include "DNA_sequence_types.h"
@@ -621,6 +622,14 @@ static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, Tree
else if (md->type == eModifierType_Hook) {
outliner_add_element(soops, &ten->subtree, ((HookModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
}
+ else if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystem *psys = ((ParticleSystemModifierData *) md)->psys;
+ TreeElement *ten_psys;
+
+ ten_psys = outliner_add_element(soops, &ten->subtree, ob, te, TSE_LINKED_PSYS, 0);
+ ten_psys->directdata = psys;
+ ten_psys->name = psys->part->id.name + 2;
+ }
}
}
diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c
index f199820dd10..5e7060d6651 100644
--- a/source/blender/editors/space_time/space_time.c
+++ b/source/blender/editors/space_time/space_time.c
@@ -50,6 +50,7 @@
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
+#include "BKE_pointcache.h"
#include "ED_anim_api.h"
#include "ED_keyframes_draw.h"
@@ -113,6 +114,162 @@ static void time_draw_sfra_efra(Scene *scene, View2D *v2d)
immUnbindProgram();
}
+static void time_draw_cache(SpaceTime *stime, Object *ob, Scene *scene)
+{
+ PTCacheID *pid;
+ ListBase pidlist;
+ SpaceTimeCache *stc = stime->caches.first;
+ const float cache_draw_height = (4.0f * UI_DPI_FAC * U.pixelsize);
+ float yoffs = 0.f;
+
+ if (!(stime->cache_display & TIME_CACHE_DISPLAY) || (!ob))
+ return;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, scene, 0);
+
+ /* iterate over pointcaches on the active object,
+ * add spacetimecache and vertex array for each */
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ float col[4], *fp;
+ int i, sta = pid->cache->startframe, end = pid->cache->endframe;
+ int len = (end - sta + 1) * 4;
+
+ switch (pid->type) {
+ case PTCACHE_TYPE_SOFTBODY:
+ if (!(stime->cache_display & TIME_CACHE_SOFTBODY)) continue;
+ break;
+ case PTCACHE_TYPE_PARTICLES:
+ if (!(stime->cache_display & TIME_CACHE_PARTICLES)) continue;
+ break;
+ case PTCACHE_TYPE_CLOTH:
+ if (!(stime->cache_display & TIME_CACHE_CLOTH)) continue;
+ break;
+ case PTCACHE_TYPE_SMOKE_DOMAIN:
+ case PTCACHE_TYPE_SMOKE_HIGHRES:
+ if (!(stime->cache_display & TIME_CACHE_SMOKE)) continue;
+ break;
+ case PTCACHE_TYPE_DYNAMICPAINT:
+ if (!(stime->cache_display & TIME_CACHE_DYNAMICPAINT)) continue;
+ break;
+ case PTCACHE_TYPE_RIGIDBODY:
+ if (!(stime->cache_display & TIME_CACHE_RIGIDBODY)) continue;
+ break;
+ }
+
+ if (pid->cache->cached_frames == NULL)
+ continue;
+
+ /* make sure we have stc with correct array length */
+ if (stc == NULL || MEM_allocN_len(stc->array) != len * 2 * sizeof(float)) {
+ if (stc) {
+ MEM_freeN(stc->array);
+ }
+ else {
+ stc = MEM_callocN(sizeof(SpaceTimeCache), "spacetimecache");
+ BLI_addtail(&stime->caches, stc);
+ }
+
+ stc->array = MEM_callocN(len * 2 * sizeof(float), "SpaceTimeCache array");
+ }
+
+ /* fill the vertex array with a quad for each cached frame */
+ for (i = sta, fp = stc->array; i <= end; i++) {
+ if (pid->cache->cached_frames[i - sta]) {
+ fp[0] = (float)i - 0.5f;
+ fp[1] = 0.0;
+ fp += 2;
+
+ fp[0] = (float)i - 0.5f;
+ fp[1] = 1.0;
+ fp += 2;
+
+ fp[0] = (float)i + 0.5f;
+ fp[1] = 1.0;
+ fp += 2;
+
+ fp[0] = (float)i + 0.5f;
+ fp[1] = 0.0;
+ fp += 2;
+ }
+ }
+
+ glPushMatrix();
+ glTranslatef(0.0, (float)V2D_SCROLL_HEIGHT + yoffs, 0.0);
+ glScalef(1.0, cache_draw_height, 0.0);
+
+ switch (pid->type) {
+ case PTCACHE_TYPE_SOFTBODY:
+ col[0] = 1.0; col[1] = 0.4; col[2] = 0.02;
+ col[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_PARTICLES:
+ col[0] = 1.0; col[1] = 0.1; col[2] = 0.02;
+ col[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_CLOTH:
+ col[0] = 0.1; col[1] = 0.1; col[2] = 0.75;
+ col[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_SMOKE_DOMAIN:
+ case PTCACHE_TYPE_SMOKE_HIGHRES:
+ col[0] = 0.2; col[1] = 0.2; col[2] = 0.2;
+ col[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_DYNAMICPAINT:
+ col[0] = 1.0; col[1] = 0.1; col[2] = 0.75;
+ col[3] = 0.1;
+ break;
+ case PTCACHE_TYPE_RIGIDBODY:
+ col[0] = 1.0; col[1] = 0.6; col[2] = 0.0;
+ col[3] = 0.1;
+ break;
+ default:
+ col[0] = 1.0; col[1] = 0.0; col[2] = 1.0;
+ col[3] = 0.1;
+ BLI_assert(0);
+ break;
+ }
+ glColor4fv(col);
+
+ glEnable(GL_BLEND);
+
+ glRectf((float)sta, 0.0, (float)end, 1.0);
+
+ col[3] = 0.4f;
+ if (pid->cache->flag & PTCACHE_BAKED) {
+ col[0] -= 0.4f; col[1] -= 0.4f; col[2] -= 0.4f;
+ }
+ else if (pid->cache->flag & PTCACHE_OUTDATED) {
+ col[0] += 0.4f; col[1] += 0.4f; col[2] += 0.4f;
+ }
+ glColor4fv(col);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, stc->array);
+ glDrawArrays(GL_QUADS, 0, (fp - stc->array) / 2);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_BLEND);
+
+ glPopMatrix();
+
+ yoffs += cache_draw_height;
+
+ stc = stc->next;
+ }
+
+ BLI_freelistN(&pidlist);
+
+ /* free excessive caches */
+ while (stc) {
+ SpaceTimeCache *tmp = stc->next;
+ BLI_remlink(&stime->caches, stc);
+ MEM_freeN(stc->array);
+ MEM_freeN(stc);
+ stc = tmp;
+ }
+}
+
static void time_cache_free(SpaceTime *stime)
{
SpaceTimeCache *stc;
@@ -377,6 +534,7 @@ static void time_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
case ND_BONE_ACTIVE:
case ND_POINTCACHE:
case ND_MODIFIER:
+ case ND_PARTICLE:
case ND_KEYS:
ED_area_tag_refresh(sa);
ED_area_tag_redraw(sa);
@@ -451,6 +609,7 @@ static void time_main_region_draw(const bContext *C, ARegion *ar)
/* draw entirely, view changes should be handled here */
Scene *scene = CTX_data_scene(C);
SpaceTime *stime = CTX_wm_space_time(C);
+ Object *obact = CTX_data_active_object(C);
View2D *v2d = &ar->v2d;
View2DGrid *grid;
View2DScrollers *scrollers;
@@ -488,6 +647,9 @@ static void time_main_region_draw(const bContext *C, ARegion *ar)
UI_view2d_view_orthoSpecial(ar, v2d, 1);
ED_markers_draw(C, 0);
+ /* caches */
+ time_draw_cache(stime, obact, scene);
+
/* callback */
UI_view2d_view_ortho(v2d);
ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 8b2738914d9..46d682233ed 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -37,7 +37,6 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
-#include "DNA_object_force.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_smoke_types.h"
@@ -72,6 +71,8 @@
#include "BKE_movieclip.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_subsurf.h"
#include "BKE_unit.h"
@@ -94,6 +95,7 @@
#include "GPU_matrix.h"
#include "ED_mesh.h"
+#include "ED_particle.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "ED_types.h"
@@ -4737,7 +4739,12 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
* with the background. */
if ((dflag & DRAW_CONSTCOLOR) == 0) {
- glColor3ubv(ob_wire_col);
+ if (is_obact && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
+ ob_wire_color_blend_theme_id(ob_wire_col, TH_BACK, 0.15f);
+ }
+ else {
+ glColor3ubv(ob_wire_col);
+ }
}
/* If drawing wire and drawtype is not OB_WIRE then we are
@@ -5784,6 +5791,1113 @@ static bool drawDispList(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *ba
}
/* *********** drawing for particles ************* */
+static void draw_particle_arrays(int draw_as, int totpoint, int ob_dt, int select)
+{
+ /* draw created data arrays */
+ switch (draw_as) {
+ case PART_DRAW_AXIS:
+ case PART_DRAW_CROSS:
+ glDrawArrays(GL_LINES, 0, 6 * totpoint);
+ break;
+ case PART_DRAW_LINE:
+ glDrawArrays(GL_LINES, 0, 2 * totpoint);
+ break;
+ case PART_DRAW_BB:
+ if (ob_dt <= OB_WIRE || select)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ else
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ glDrawArrays(GL_QUADS, 0, 4 * totpoint);
+ break;
+ default:
+ glDrawArrays(GL_POINTS, 0, totpoint);
+ break;
+ }
+}
+static void draw_particle(ParticleKey *state, int draw_as, short draw, float pixsize,
+ float imat[4][4], const float draw_line[2], ParticleBillboardData *bb, ParticleDrawData *pdd)
+{
+ float vec[3], vec2[3];
+ float *vd = NULL;
+ float *cd = NULL;
+ float ma_col[3] = {0.0f, 0.0f, 0.0f};
+
+ /* null only for PART_DRAW_CIRC */
+ if (pdd) {
+ vd = pdd->vd;
+ cd = pdd->cd;
+
+ if (pdd->ma_col) {
+ copy_v3_v3(ma_col, pdd->ma_col);
+ }
+ }
+
+ switch (draw_as) {
+ case PART_DRAW_DOT:
+ {
+ if (vd) {
+ copy_v3_v3(vd, state->co); pdd->vd += 3;
+ }
+ if (cd) {
+ copy_v3_v3(cd, pdd->ma_col);
+ pdd->cd += 3;
+ }
+ break;
+ }
+ case PART_DRAW_CROSS:
+ case PART_DRAW_AXIS:
+ {
+ vec[0] = 2.0f * pixsize;
+ vec[1] = vec[2] = 0.0;
+ mul_qt_v3(state->rot, vec);
+ if (draw_as == PART_DRAW_AXIS) {
+ if (cd) {
+ cd[1] = cd[2] = cd[4] = cd[5] = 0.0;
+ cd[0] = cd[3] = 1.0;
+ cd[6] = cd[8] = cd[9] = cd[11] = 0.0;
+ cd[7] = cd[10] = 1.0;
+ cd[13] = cd[12] = cd[15] = cd[16] = 0.0;
+ cd[14] = cd[17] = 1.0;
+ pdd->cd += 18;
+ }
+
+ copy_v3_v3(vec2, state->co);
+ }
+ else {
+ if (cd) {
+ cd[0] = cd[3] = cd[6] = cd[9] = cd[12] = cd[15] = ma_col[0];
+ cd[1] = cd[4] = cd[7] = cd[10] = cd[13] = cd[16] = ma_col[1];
+ cd[2] = cd[5] = cd[8] = cd[11] = cd[14] = cd[17] = ma_col[2];
+ pdd->cd += 18;
+ }
+ sub_v3_v3v3(vec2, state->co, vec);
+ }
+
+ add_v3_v3(vec, state->co);
+ copy_v3_v3(pdd->vd, vec); pdd->vd += 3;
+ copy_v3_v3(pdd->vd, vec2); pdd->vd += 3;
+
+ vec[1] = 2.0f * pixsize;
+ vec[0] = vec[2] = 0.0;
+ mul_qt_v3(state->rot, vec);
+ if (draw_as == PART_DRAW_AXIS) {
+ copy_v3_v3(vec2, state->co);
+ }
+ else {
+ sub_v3_v3v3(vec2, state->co, vec);
+ }
+
+ add_v3_v3(vec, state->co);
+ copy_v3_v3(pdd->vd, vec); pdd->vd += 3;
+ copy_v3_v3(pdd->vd, vec2); pdd->vd += 3;
+
+ vec[2] = 2.0f * pixsize;
+ vec[0] = vec[1] = 0.0f;
+ mul_qt_v3(state->rot, vec);
+ if (draw_as == PART_DRAW_AXIS) {
+ copy_v3_v3(vec2, state->co);
+ }
+ else {
+ sub_v3_v3v3(vec2, state->co, vec);
+ }
+
+ add_v3_v3(vec, state->co);
+
+ copy_v3_v3(pdd->vd, vec); pdd->vd += 3;
+ copy_v3_v3(pdd->vd, vec2); pdd->vd += 3;
+ break;
+ }
+ case PART_DRAW_LINE:
+ {
+ copy_v3_v3(vec, state->vel);
+ normalize_v3(vec);
+ if (draw & PART_DRAW_VEL_LENGTH)
+ mul_v3_fl(vec, len_v3(state->vel));
+ madd_v3_v3v3fl(pdd->vd, state->co, vec, -draw_line[0]); pdd->vd += 3;
+ madd_v3_v3v3fl(pdd->vd, state->co, vec, draw_line[1]); pdd->vd += 3;
+ if (cd) {
+ cd[0] = cd[3] = ma_col[0];
+ cd[1] = cd[4] = ma_col[1];
+ cd[2] = cd[5] = ma_col[2];
+ pdd->cd += 6;
+ }
+ break;
+ }
+ case PART_DRAW_CIRC:
+ {
+ drawcircball(GL_LINE_LOOP, state->co, pixsize, imat);
+ break;
+ }
+ case PART_DRAW_BB:
+ {
+ float xvec[3], yvec[3], zvec[3], bb_center[3];
+ if (cd) {
+ cd[0] = cd[3] = cd[6] = cd[9] = ma_col[0];
+ cd[1] = cd[4] = cd[7] = cd[10] = ma_col[1];
+ cd[2] = cd[5] = cd[8] = cd[11] = ma_col[2];
+ pdd->cd += 12;
+ }
+
+ copy_v3_v3(bb->vec, state->co);
+ copy_v3_v3(bb->vel, state->vel);
+
+ psys_make_billboard(bb, xvec, yvec, zvec, bb_center);
+
+ add_v3_v3v3(pdd->vd, bb_center, xvec);
+ add_v3_v3(pdd->vd, yvec); pdd->vd += 3;
+
+ sub_v3_v3v3(pdd->vd, bb_center, xvec);
+ add_v3_v3(pdd->vd, yvec); pdd->vd += 3;
+
+ sub_v3_v3v3(pdd->vd, bb_center, xvec);
+ sub_v3_v3v3(pdd->vd, pdd->vd, yvec); pdd->vd += 3;
+
+ add_v3_v3v3(pdd->vd, bb_center, xvec);
+ sub_v3_v3v3(pdd->vd, pdd->vd, yvec); pdd->vd += 3;
+
+ copy_v3_v3(pdd->nd, zvec); pdd->nd += 3;
+ copy_v3_v3(pdd->nd, zvec); pdd->nd += 3;
+ copy_v3_v3(pdd->nd, zvec); pdd->nd += 3;
+ copy_v3_v3(pdd->nd, zvec); pdd->nd += 3;
+ break;
+ }
+ }
+}
+static void draw_particle_data(ParticleSystem *psys, RegionView3D *rv3d,
+ ParticleKey *state, int draw_as,
+ float imat[4][4], ParticleBillboardData *bb, ParticleDrawData *pdd,
+ const float ct, const float pa_size, const float r_tilt, const float pixsize_scale)
+{
+ ParticleSettings *part = psys->part;
+ float pixsize;
+
+ if (psys->parent)
+ mul_m4_v3(psys->parent->obmat, state->co);
+
+ /* create actual particle data */
+ if (draw_as == PART_DRAW_BB) {
+ bb->offset[0] = part->bb_offset[0];
+ bb->offset[1] = part->bb_offset[1];
+ bb->size[0] = part->bb_size[0] * pa_size;
+ if (part->bb_align == PART_BB_VEL) {
+ float pa_vel = len_v3(state->vel);
+ float head = part->bb_vel_head * pa_vel;
+ float tail = part->bb_vel_tail * pa_vel;
+ bb->size[1] = part->bb_size[1] * pa_size + head + tail;
+ /* use offset to adjust the particle center. this is relative to size, so need to divide! */
+ if (bb->size[1] > 0.0f)
+ bb->offset[1] += (head - tail) / bb->size[1];
+ }
+ else {
+ bb->size[1] = part->bb_size[1] * pa_size;
+ }
+ bb->tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
+ bb->time = ct;
+ }
+
+ pixsize = ED_view3d_pixel_size(rv3d, state->co) * pixsize_scale;
+
+ draw_particle(state, draw_as, part->draw, pixsize, imat, part->draw_line, bb, pdd);
+}
+/* unified drawing of all new particle systems draw types except dupli ob & group
+ * mostly tries to use vertex arrays for speed
+ *
+ * 1. check that everything is ok & updated
+ * 2. start initializing things
+ * 3. initialize according to draw type
+ * 4. allocate drawing data arrays
+ * 5. start filling the arrays
+ * 6. draw the arrays
+ * 7. clean up
+ */
+static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv3d,
+ Base *base, ParticleSystem *psys,
+ const char ob_dt, const short dflag)
+{
+ Object *ob = base->object;
+ ParticleEditSettings *pset = PE_settings(scene);
+ ParticleSettings *part = psys->part;
+ ParticleData *pars = psys->particles;
+ ParticleData *pa;
+ ParticleKey state, *states = NULL;
+ ParticleBillboardData bb;
+ ParticleSimulationData sim = {NULL};
+ ParticleDrawData *pdd = psys->pdd;
+ Material *ma;
+ float vel[3], imat[4][4];
+ float timestep, pixsize_scale = 1.0f, pa_size, r_tilt, r_length;
+ float pa_time, pa_birthtime, pa_dietime, pa_health, intensity;
+ float cfra;
+ float ma_col[3] = {0.0f, 0.0f, 0.0f};
+ int a, totpart, totpoint = 0, totve = 0, drawn, draw_as, totchild = 0;
+ bool select = (ob->flag & SELECT) != 0, create_cdata = false, need_v = false;
+ GLint polygonmode[2];
+ char numstr[32];
+ unsigned char tcol[4] = {0, 0, 0, 255};
+
+/* 1. */
+ if (part == NULL || !psys_check_enabled(ob, psys, G.is_rendering))
+ return;
+
+ if (pars == NULL) return;
+
+ /* don't draw normal paths in edit mode */
+ if (psys_in_edit_mode(scene, psys) && (pset->flag & PE_DRAW_PART) == 0)
+ return;
+
+ if (part->draw_as == PART_DRAW_REND)
+ draw_as = part->ren_as;
+ else
+ draw_as = part->draw_as;
+
+ if (draw_as == PART_DRAW_NOT)
+ return;
+
+ /* prepare curvemapping tables */
+ if ((psys->part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && psys->part->clumpcurve)
+ curvemapping_changed_all(psys->part->clumpcurve);
+ if ((psys->part->child_flag & PART_CHILD_USE_ROUGH_CURVE) && psys->part->roughcurve)
+ curvemapping_changed_all(psys->part->roughcurve);
+
+/* 2. */
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(ob, psys);
+
+ if (part->phystype == PART_PHYS_KEYED) {
+ if (psys->flag & PSYS_KEYED) {
+ psys_count_keyed_targets(&sim);
+ if (psys->totkeyed == 0)
+ return;
+ }
+ }
+
+ if (select) {
+ select = false;
+ if (psys_get_current(ob) == psys)
+ select = true;
+ }
+
+ psys->flag |= PSYS_DRAWING;
+
+ if (part->type == PART_HAIR && !psys->childcache)
+ totchild = 0;
+ else
+ totchild = psys->totchild * part->disp / 100;
+
+ ma = give_current_material(ob, part->omat);
+
+ if (v3d->zbuf) glDepthMask(1);
+
+ if ((ma) && (part->draw_col == PART_DRAW_COL_MAT)) {
+ rgb_float_to_uchar(tcol, &(ma->r));
+ copy_v3_v3(ma_col, &ma->r);
+ }
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ glColor3ubv(tcol);
+ }
+
+ timestep = psys_get_timestep(&sim);
+
+ if ((base->flag & OB_FROMDUPLI) && (ob->flag & OB_FROMGROUP)) {
+ float mat[4][4];
+ mul_m4_m4m4(mat, ob->obmat, psys->imat);
+ glMultMatrixf(mat);
+ }
+
+ /* needed for text display */
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ totpart = psys->totpart;
+
+ cfra = BKE_scene_frame_get(scene);
+
+ if (draw_as == PART_DRAW_PATH && psys->pathcache == NULL && psys->childcache == NULL)
+ draw_as = PART_DRAW_DOT;
+
+/* 3. */
+ glLineWidth(1.0f);
+
+ switch (draw_as) {
+ case PART_DRAW_DOT:
+ if (part->draw_size)
+ glPointSize(part->draw_size);
+ else
+ glPointSize(2.0); /* default dot size */
+ break;
+ case PART_DRAW_CIRC:
+ /* calculate view aligned matrix: */
+ copy_m4_m4(imat, rv3d->viewinv);
+ normalize_v3(imat[0]);
+ normalize_v3(imat[1]);
+ /* fall-through */
+ case PART_DRAW_CROSS:
+ case PART_DRAW_AXIS:
+ /* lets calculate the scale: */
+
+ if (part->draw_size == 0.0)
+ pixsize_scale = 2.0f;
+ else
+ pixsize_scale = part->draw_size;
+
+ if (draw_as == PART_DRAW_AXIS)
+ create_cdata = 1;
+ break;
+ case PART_DRAW_OB:
+ if (part->dup_ob == NULL)
+ draw_as = PART_DRAW_DOT;
+ else
+ draw_as = 0;
+ break;
+ case PART_DRAW_GR:
+ if (part->dup_group == NULL)
+ draw_as = PART_DRAW_DOT;
+ else
+ draw_as = 0;
+ break;
+ case PART_DRAW_BB:
+ if (v3d->camera == NULL && part->bb_ob == NULL) {
+ printf("Billboards need an active camera or a target object!\n");
+
+ draw_as = part->draw_as = PART_DRAW_DOT;
+
+ if (part->draw_size)
+ glPointSize(part->draw_size);
+ else
+ glPointSize(2.0); /* default dot size */
+ }
+ else if (part->bb_ob)
+ bb.ob = part->bb_ob;
+ else
+ bb.ob = v3d->camera;
+
+ bb.align = part->bb_align;
+ bb.anim = part->bb_anim;
+ bb.lock = part->draw & PART_DRAW_BB_LOCK;
+ break;
+ case PART_DRAW_PATH:
+ break;
+ case PART_DRAW_LINE:
+ need_v = 1;
+ break;
+ }
+ if (part->draw & PART_DRAW_SIZE && part->draw_as != PART_DRAW_CIRC) {
+ copy_m4_m4(imat, rv3d->viewinv);
+ normalize_v3(imat[0]);
+ normalize_v3(imat[1]);
+ }
+
+ if (ELEM(draw_as, PART_DRAW_DOT, PART_DRAW_CROSS, PART_DRAW_LINE) &&
+ (part->draw_col > PART_DRAW_COL_MAT))
+ {
+ create_cdata = 1;
+ }
+
+ if (!create_cdata && pdd && pdd->cdata) {
+ MEM_freeN(pdd->cdata);
+ pdd->cdata = pdd->cd = NULL;
+ }
+
+/* 4. */
+ if (draw_as && ELEM(draw_as, PART_DRAW_PATH, PART_DRAW_CIRC) == 0) {
+ int tot_vec_size = (totpart + totchild) * 3 * sizeof(float);
+ int create_ndata = 0;
+
+ if (!pdd)
+ pdd = psys->pdd = MEM_callocN(sizeof(ParticleDrawData), "ParticleDrawData");
+
+ if (part->draw_as == PART_DRAW_REND && part->trail_count > 1) {
+ tot_vec_size *= part->trail_count;
+ psys_make_temp_pointcache(ob, psys);
+ }
+
+ switch (draw_as) {
+ case PART_DRAW_AXIS:
+ case PART_DRAW_CROSS:
+ tot_vec_size *= 6;
+ if (draw_as != PART_DRAW_CROSS)
+ create_cdata = 1;
+ break;
+ case PART_DRAW_LINE:
+ tot_vec_size *= 2;
+ break;
+ case PART_DRAW_BB:
+ tot_vec_size *= 4;
+ create_ndata = 1;
+ break;
+ }
+
+ if (pdd->tot_vec_size != tot_vec_size)
+ psys_free_pdd(psys);
+
+ if (!pdd->vdata)
+ pdd->vdata = MEM_callocN(tot_vec_size, "particle_vdata");
+ if (create_cdata && !pdd->cdata)
+ pdd->cdata = MEM_callocN(tot_vec_size, "particle_cdata");
+ if (create_ndata && !pdd->ndata)
+ pdd->ndata = MEM_callocN(tot_vec_size, "particle_ndata");
+
+ if (part->draw & PART_DRAW_VEL && draw_as != PART_DRAW_LINE) {
+ if (!pdd->vedata)
+ pdd->vedata = MEM_callocN(2 * (totpart + totchild) * 3 * sizeof(float), "particle_vedata");
+
+ need_v = 1;
+ }
+ else if (pdd->vedata) {
+ /* velocity data not needed, so free it */
+ MEM_freeN(pdd->vedata);
+ pdd->vedata = NULL;
+ }
+
+ pdd->vd = pdd->vdata;
+ pdd->ved = pdd->vedata;
+ pdd->cd = pdd->cdata;
+ pdd->nd = pdd->ndata;
+ pdd->tot_vec_size = tot_vec_size;
+ }
+ else if (psys->pdd) {
+ psys_free_pdd(psys);
+ MEM_freeN(psys->pdd);
+ pdd = psys->pdd = NULL;
+ }
+
+ if (pdd) {
+ pdd->ma_col = ma_col;
+ }
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ /* circles don't use drawdata, so have to add a special case here */
+ if ((pdd || draw_as == PART_DRAW_CIRC) && draw_as != PART_DRAW_PATH) {
+ /* 5. */
+ if (pdd && (pdd->flag & PARTICLE_DRAW_DATA_UPDATED) &&
+ (pdd->vedata || part->draw & (PART_DRAW_SIZE | PART_DRAW_NUM | PART_DRAW_HEALTH)) == 0)
+ {
+ totpoint = pdd->totpoint; /* draw data is up to date */
+ }
+ else {
+ for (a = 0, pa = pars; a < totpart + totchild; a++, pa++) {
+ /* setup per particle individual stuff */
+ if (a < totpart) {
+ if (totchild && (part->draw & PART_DRAW_PARENT) == 0) continue;
+ if (pa->flag & PARS_NO_DISP || pa->flag & PARS_UNEXIST) continue;
+
+ pa_time = (cfra - pa->time) / pa->lifetime;
+ pa_birthtime = pa->time;
+ pa_dietime = pa->dietime;
+ pa_size = pa->size;
+ if (part->phystype == PART_PHYS_BOIDS)
+ pa_health = pa->boid->data.health;
+ else
+ pa_health = -1.0;
+
+ r_tilt = 2.0f * (psys_frand(psys, a + 21) - 0.5f);
+ r_length = psys_frand(psys, a + 22);
+
+ if (part->draw_col > PART_DRAW_COL_MAT) {
+ switch (part->draw_col) {
+ case PART_DRAW_COL_VEL:
+ intensity = len_v3(pa->state.vel) / part->color_vec_max;
+ break;
+ case PART_DRAW_COL_ACC:
+ intensity = len_v3v3(pa->state.vel, pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * part->color_vec_max);
+ break;
+ default:
+ intensity = 1.0f; /* should never happen */
+ BLI_assert(0);
+ break;
+ }
+ CLAMP(intensity, 0.0f, 1.0f);
+ weight_to_rgb(ma_col, intensity);
+ }
+ }
+ else {
+ ChildParticle *cpa = &psys->child[a - totpart];
+
+ pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);
+ pa_size = psys_get_child_size(psys, cpa, cfra, NULL);
+
+ pa_health = -1.0;
+
+ r_tilt = 2.0f * (psys_frand(psys, a + 21) - 0.5f);
+ r_length = psys_frand(psys, a + 22);
+ }
+
+ drawn = 0;
+ if (part->draw_as == PART_DRAW_REND && part->trail_count > 1) {
+ float length = part->path_end * (1.0f - part->randlength * r_length);
+ int trail_count = part->trail_count * (1.0f - part->randlength * r_length);
+ float ct = ((part->draw & PART_ABS_PATH_TIME) ? cfra : pa_time) - length;
+ float dt = length / (trail_count ? (float)trail_count : 1.0f);
+ int i = 0;
+
+ ct += dt;
+ for (i = 0; i < trail_count; i++, ct += dt) {
+
+ if (part->draw & PART_ABS_PATH_TIME) {
+ if (ct < pa_birthtime || ct > pa_dietime)
+ continue;
+ }
+ else if (ct < 0.0f || ct > 1.0f)
+ continue;
+
+ state.time = (part->draw & PART_ABS_PATH_TIME) ? -ct : -(pa_birthtime + ct * (pa_dietime - pa_birthtime));
+ psys_get_particle_on_path(&sim, a, &state, need_v);
+
+ draw_particle_data(psys, rv3d,
+ &state, draw_as, imat, &bb, psys->pdd,
+ ct, pa_size, r_tilt, pixsize_scale);
+
+ totpoint++;
+ drawn = 1;
+ }
+ }
+ else {
+ state.time = cfra;
+ if (psys_get_particle_state(&sim, a, &state, 0)) {
+
+ draw_particle_data(psys, rv3d,
+ &state, draw_as, imat, &bb, psys->pdd,
+ pa_time, pa_size, r_tilt, pixsize_scale);
+
+ totpoint++;
+ drawn = 1;
+ }
+ }
+
+ if (drawn) {
+ /* additional things to draw for each particle
+ * (velocity, size and number) */
+ if ((part->draw & PART_DRAW_VEL) && pdd && pdd->vedata) {
+ copy_v3_v3(pdd->ved, state.co);
+ pdd->ved += 3;
+ mul_v3_v3fl(vel, state.vel, timestep);
+ add_v3_v3v3(pdd->ved, state.co, vel);
+ pdd->ved += 3;
+
+ totve++;
+ }
+
+ if (part->draw & PART_DRAW_SIZE) {
+ setlinestyle(3);
+ drawcircball(GL_LINE_LOOP, state.co, pa_size, imat);
+ setlinestyle(0);
+ }
+
+
+ if ((part->draw & PART_DRAW_NUM || part->draw & PART_DRAW_HEALTH) &&
+ (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0)
+ {
+ size_t numstr_len;
+ float vec_txt[3];
+ char *val_pos = numstr;
+ numstr[0] = '\0';
+
+ if (part->draw & PART_DRAW_NUM) {
+ if (a < totpart && (part->draw & PART_DRAW_HEALTH) && (part->phystype == PART_PHYS_BOIDS)) {
+ numstr_len = BLI_snprintf_rlen(val_pos, sizeof(numstr), "%d:%.2f", a, pa_health);
+ }
+ else {
+ numstr_len = BLI_snprintf_rlen(val_pos, sizeof(numstr), "%d", a);
+ }
+ }
+ else {
+ if (a < totpart && (part->draw & PART_DRAW_HEALTH) && (part->phystype == PART_PHYS_BOIDS)) {
+ numstr_len = BLI_snprintf_rlen(val_pos, sizeof(numstr), "%.2f", pa_health);
+ }
+ }
+
+ if (numstr[0]) {
+ /* in path drawing state.co is the end point
+ * use worldspace because object matrix is already applied */
+ mul_v3_m4v3(vec_txt, ob->imat, state.co);
+ view3d_cached_text_draw_add(vec_txt, numstr, numstr_len,
+ 10, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, tcol);
+ }
+ }
+ }
+ }
+ }
+ }
+/* 6. */
+
+ glGetIntegerv(GL_POLYGON_MODE, polygonmode);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ if (draw_as == PART_DRAW_PATH) {
+ ParticleCacheKey **cache, *path;
+ float *cdata2 = NULL;
+
+ /* setup gl flags */
+ if (1) { //ob_dt > OB_WIRE) {
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ if (part->draw_col == PART_DRAW_COL_MAT)
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ // XXX test
+ GPU_basic_shader_colors(NULL, NULL, 0.0f, 1.0f);
+ GPU_basic_shader_bind(GPU_SHADER_LIGHTING | GPU_SHADER_USE_COLOR);
+ }
+
+ if (totchild && (part->draw & PART_DRAW_PARENT) == 0)
+ totpart = 0;
+ else if (psys->pathcache == NULL)
+ totpart = 0;
+
+ /* draw actual/parent particles */
+ cache = psys->pathcache;
+ for (a = 0, pa = psys->particles; a < totpart; a++, pa++) {
+ path = cache[a];
+ if (path->segments > 0) {
+ glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co);
+
+ if (1) { //ob_dt > OB_WIRE) {
+ glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel);
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ if (part->draw_col == PART_DRAW_COL_MAT) {
+ glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+ }
+ }
+ }
+
+ glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1);
+ }
+ }
+
+ if (part->type == PART_HAIR) {
+ if (part->draw & PART_DRAW_GUIDE_HAIRS) {
+ DerivedMesh *hair_dm = psys->hair_out_dm;
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ for (a = 0, pa = psys->particles; a < totpart; a++, pa++) {
+ if (pa->totkey > 1) {
+ HairKey *hkey = pa->hair;
+
+ glVertexPointer(3, GL_FLOAT, sizeof(HairKey), hkey->world_co);
+
+#if 0 /* XXX use proper theme color here */
+ UI_ThemeColor(TH_NORMAL);
+#else
+ glColor3f(0.58f, 0.67f, 1.0f);
+#endif
+
+ glDrawArrays(GL_LINE_STRIP, 0, pa->totkey);
+ }
+ }
+
+ if (hair_dm) {
+ MVert *mvert = hair_dm->getVertArray(hair_dm);
+ int i;
+
+ glColor3f(0.9f, 0.4f, 0.4f);
+
+ glBegin(GL_LINES);
+ for (a = 0, pa = psys->particles; a < totpart; a++, pa++) {
+ for (i = 1; i < pa->totkey; ++i) {
+ float v1[3], v2[3];
+
+ copy_v3_v3(v1, mvert[pa->hair_index + i - 1].co);
+ copy_v3_v3(v2, mvert[pa->hair_index + i].co);
+
+ mul_m4_v3(ob->obmat, v1);
+ mul_m4_v3(ob->obmat, v2);
+
+ glVertex3fv(v1);
+ glVertex3fv(v2);
+ }
+ }
+ glEnd();
+ }
+
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if ((dflag & DRAW_CONSTCOLOR) == 0)
+ if (part->draw_col == PART_DRAW_COL_MAT)
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ if (part->draw & PART_DRAW_HAIR_GRID) {
+ ClothModifierData *clmd = psys->clmd;
+ if (clmd) {
+ float *gmin = clmd->hair_grid_min;
+ float *gmax = clmd->hair_grid_max;
+ int *res = clmd->hair_grid_res;
+ int i;
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ if (select)
+ UI_ThemeColor(TH_ACTIVE);
+ else
+ UI_ThemeColor(TH_WIRE);
+ glBegin(GL_LINES);
+ glVertex3f(gmin[0], gmin[1], gmin[2]); glVertex3f(gmax[0], gmin[1], gmin[2]);
+ glVertex3f(gmax[0], gmin[1], gmin[2]); glVertex3f(gmax[0], gmax[1], gmin[2]);
+ glVertex3f(gmax[0], gmax[1], gmin[2]); glVertex3f(gmin[0], gmax[1], gmin[2]);
+ glVertex3f(gmin[0], gmax[1], gmin[2]); glVertex3f(gmin[0], gmin[1], gmin[2]);
+
+ glVertex3f(gmin[0], gmin[1], gmax[2]); glVertex3f(gmax[0], gmin[1], gmax[2]);
+ glVertex3f(gmax[0], gmin[1], gmax[2]); glVertex3f(gmax[0], gmax[1], gmax[2]);
+ glVertex3f(gmax[0], gmax[1], gmax[2]); glVertex3f(gmin[0], gmax[1], gmax[2]);
+ glVertex3f(gmin[0], gmax[1], gmax[2]); glVertex3f(gmin[0], gmin[1], gmax[2]);
+
+ glVertex3f(gmin[0], gmin[1], gmin[2]); glVertex3f(gmin[0], gmin[1], gmax[2]);
+ glVertex3f(gmax[0], gmin[1], gmin[2]); glVertex3f(gmax[0], gmin[1], gmax[2]);
+ glVertex3f(gmin[0], gmax[1], gmin[2]); glVertex3f(gmin[0], gmax[1], gmax[2]);
+ glVertex3f(gmax[0], gmax[1], gmin[2]); glVertex3f(gmax[0], gmax[1], gmax[2]);
+ glEnd();
+
+ if (select)
+ UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -100);
+ else
+ UI_ThemeColorShadeAlpha(TH_WIRE, 0, -100);
+ glEnable(GL_BLEND);
+ glBegin(GL_LINES);
+ for (i = 1; i < res[0] - 1; ++i) {
+ float f = interpf(gmax[0], gmin[0], (float)i / (float)(res[0] - 1));
+ glVertex3f(f, gmin[1], gmin[2]); glVertex3f(f, gmax[1], gmin[2]);
+ glVertex3f(f, gmax[1], gmin[2]); glVertex3f(f, gmax[1], gmax[2]);
+ glVertex3f(f, gmax[1], gmax[2]); glVertex3f(f, gmin[1], gmax[2]);
+ glVertex3f(f, gmin[1], gmax[2]); glVertex3f(f, gmin[1], gmin[2]);
+ }
+ for (i = 1; i < res[1] - 1; ++i) {
+ float f = interpf(gmax[1], gmin[1], (float)i / (float)(res[1] - 1));
+ glVertex3f(gmin[0], f, gmin[2]); glVertex3f(gmax[0], f, gmin[2]);
+ glVertex3f(gmax[0], f, gmin[2]); glVertex3f(gmax[0], f, gmax[2]);
+ glVertex3f(gmax[0], f, gmax[2]); glVertex3f(gmin[0], f, gmax[2]);
+ glVertex3f(gmin[0], f, gmax[2]); glVertex3f(gmin[0], f, gmin[2]);
+ }
+ for (i = 1; i < res[2] - 1; ++i) {
+ float f = interpf(gmax[2], gmin[2], (float)i / (float)(res[2] - 1));
+ glVertex3f(gmin[0], gmin[1], f); glVertex3f(gmax[0], gmin[1], f);
+ glVertex3f(gmax[0], gmin[1], f); glVertex3f(gmax[0], gmax[1], f);
+ glVertex3f(gmax[0], gmax[1], f); glVertex3f(gmin[0], gmax[1], f);
+ glVertex3f(gmin[0], gmax[1], f); glVertex3f(gmin[0], gmin[1], f);
+ }
+ glEnd();
+ glDisable(GL_BLEND);
+
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if ((dflag & DRAW_CONSTCOLOR) == 0)
+ if (part->draw_col == PART_DRAW_COL_MAT)
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+ }
+ }
+
+ /* draw child particles */
+ cache = psys->childcache;
+ for (a = 0; a < totchild; a++) {
+ path = cache[a];
+ glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co);
+
+ if (1) { //ob_dt > OB_WIRE) {
+ glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel);
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ if (part->draw_col == PART_DRAW_COL_MAT) {
+ glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+ }
+ }
+ }
+
+ glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1);
+ }
+
+ /* restore & clean up */
+ if (1) { //ob_dt > OB_WIRE) {
+ if (part->draw_col == PART_DRAW_COL_MAT)
+ glDisableClientState(GL_COLOR_ARRAY);
+ GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ }
+
+ if (cdata2) {
+ MEM_freeN(cdata2);
+ cdata2 = NULL;
+ }
+
+ if ((part->draw & PART_DRAW_NUM) && (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
+ cache = psys->pathcache;
+
+ for (a = 0, pa = psys->particles; a < totpart; a++, pa++) {
+ float vec_txt[3];
+ size_t numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%i", a);
+ /* use worldspace because object matrix is already applied */
+ mul_v3_m4v3(vec_txt, ob->imat, cache[a]->co);
+ view3d_cached_text_draw_add(vec_txt, numstr, numstr_len,
+ 10, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, tcol);
+ }
+ }
+ }
+ else if (pdd && ELEM(draw_as, 0, PART_DRAW_CIRC) == 0) {
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ /* enable point data array */
+ if (pdd->vdata) {
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, pdd->vdata);
+ }
+ else
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ if (select) {
+ UI_ThemeColor(TH_ACTIVE);
+
+ if (part->draw_size)
+ glPointSize(part->draw_size + 2);
+ else
+ glPointSize(4.0);
+
+ glLineWidth(3.0);
+
+ draw_particle_arrays(draw_as, totpoint, ob_dt, 1);
+ }
+
+ /* restore from select */
+ glColor3fv(ma_col);
+ }
+
+ glPointSize(part->draw_size ? part->draw_size : 2.0);
+ glLineWidth(1.0);
+
+ /* enable other data arrays */
+
+ /* billboards are drawn this way */
+ if (pdd->ndata && ob_dt > OB_WIRE) {
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glNormalPointer(GL_FLOAT, 0, pdd->ndata);
+ GPU_basic_shader_colors(NULL, NULL, 0.0f, 1.0f);
+ GPU_basic_shader_bind(GPU_SHADER_LIGHTING | GPU_SHADER_USE_COLOR);
+ }
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ if (pdd->cdata) {
+ glEnableClientState(GL_COLOR_ARRAY);
+ glColorPointer(3, GL_FLOAT, 0, pdd->cdata);
+ }
+ }
+
+ draw_particle_arrays(draw_as, totpoint, ob_dt, 0);
+
+ pdd->flag |= PARTICLE_DRAW_DATA_UPDATED;
+ pdd->totpoint = totpoint;
+ }
+
+ if (pdd && pdd->vedata) {
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ glDisableClientState(GL_COLOR_ARRAY);
+ cpack(0xC0C0C0);
+ }
+
+ glVertexPointer(3, GL_FLOAT, 0, pdd->vedata);
+
+ glDrawArrays(GL_LINES, 0, 2 * totve);
+ }
+
+ glPolygonMode(GL_FRONT, polygonmode[0]);
+ glPolygonMode(GL_BACK, polygonmode[1]);
+
+/* 7. */
+
+ GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ if (states)
+ MEM_freeN(states);
+
+ psys->flag &= ~PSYS_DRAWING;
+
+ /* draw data can't be saved for billboards as they must update to target changes */
+ if (draw_as == PART_DRAW_BB) {
+ psys_free_pdd(psys);
+ pdd->flag &= ~PARTICLE_DRAW_DATA_UPDATED;
+ }
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (pdd) {
+ /* drop references to stack memory */
+ pdd->ma_col = NULL;
+ }
+
+ if ((base->flag & OB_FROMDUPLI) && (ob->flag & OB_FROMGROUP)) {
+ glLoadMatrixf(rv3d->viewmat);
+ }
+}
+
+static void draw_update_ptcache_edit(Scene *scene, Object *ob, PTCacheEdit *edit)
+{
+ if (edit->psys && edit->psys->flag & PSYS_HAIR_UPDATED)
+ PE_update_object(scene, ob, 0);
+
+ /* create path and child path cache if it doesn't exist already */
+ if (edit->pathcache == NULL)
+ psys_cache_edit_paths(scene, ob, edit, CFRA, G.is_rendering);
+}
+
+static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit)
+{
+ ParticleCacheKey **cache, *path, *pkey;
+ PTCacheEditPoint *point;
+ PTCacheEditKey *key;
+ ParticleEditSettings *pset = PE_settings(scene);
+ int i, k, totpoint = edit->totpoint, timed = (pset->flag & PE_FADE_TIME) ? pset->fade_frames : 0;
+ int totkeys = 1;
+ float sel_col[3];
+ float nosel_col[3];
+ float *pathcol = NULL, *pcol;
+
+ if (edit->pathcache == NULL)
+ return;
+
+ PE_hide_keys_time(scene, edit, CFRA);
+
+ /* opengl setup */
+ if ((v3d->flag & V3D_ZBUF_SELECT) == 0)
+ glDisable(GL_DEPTH_TEST);
+
+ /* get selection theme colors */
+ UI_GetThemeColor3fv(TH_VERTEX_SELECT, sel_col);
+ UI_GetThemeColor3fv(TH_VERTEX, nosel_col);
+
+ /* draw paths */
+ totkeys = (*edit->pathcache)->segments + 1;
+
+ glEnable(GL_BLEND);
+ pathcol = MEM_callocN(totkeys * 4 * sizeof(float), "particle path color data");
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ if (pset->brushtype == PE_BRUSH_WEIGHT)
+ glLineWidth(2.0f);
+
+ cache = edit->pathcache;
+ for (i = 0, point = edit->points; i < totpoint; i++, point++) {
+ path = cache[i];
+ glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co);
+
+ if (point->flag & PEP_HIDE) {
+ for (k = 0, pcol = pathcol; k < totkeys; k++, pcol += 4) {
+ copy_v3_v3(pcol, path->col);
+ pcol[3] = 0.25f;
+ }
+
+ glColorPointer(4, GL_FLOAT, 4 * sizeof(float), pathcol);
+ }
+ else if (timed) {
+ for (k = 0, pcol = pathcol, pkey = path; k < totkeys; k++, pkey++, pcol += 4) {
+ copy_v3_v3(pcol, pkey->col);
+ pcol[3] = 1.0f - fabsf((float)(CFRA) -pkey->time) / (float)pset->fade_frames;
+ }
+
+ glColorPointer(4, GL_FLOAT, 4 * sizeof(float), pathcol);
+ }
+ else
+ glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+
+ glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1);
+ }
+
+ if (pathcol) { MEM_freeN(pathcol); pathcol = pcol = NULL; }
+
+
+ /* draw edit vertices */
+ if (pset->selectmode != SCE_SELECT_PATH) {
+ glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE));
+
+ if (pset->selectmode == SCE_SELECT_POINT) {
+ float *pd = NULL, *pdata = NULL;
+ float *cd = NULL, *cdata = NULL;
+ int totkeys_visible = 0;
+
+ for (i = 0, point = edit->points; i < totpoint; i++, point++)
+ if (!(point->flag & PEP_HIDE))
+ totkeys_visible += point->totkey;
+
+ if (totkeys_visible) {
+ if (edit->points && !(edit->points->keys->flag & PEK_USE_WCO))
+ pd = pdata = MEM_callocN(totkeys_visible * 3 * sizeof(float), "particle edit point data");
+ cd = cdata = MEM_callocN(totkeys_visible * (timed ? 4 : 3) * sizeof(float), "particle edit color data");
+ }
+
+ for (i = 0, point = edit->points; i < totpoint; i++, point++) {
+ if (point->flag & PEP_HIDE)
+ continue;
+
+ for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
+ if (pd) {
+ copy_v3_v3(pd, key->co);
+ pd += 3;
+ }
+
+ if (key->flag & PEK_SELECT) {
+ copy_v3_v3(cd, sel_col);
+ }
+ else {
+ copy_v3_v3(cd, nosel_col);
+ }
+
+ if (timed)
+ *(cd + 3) = 1.0f - fabsf((float)CFRA - *key->time) / (float)pset->fade_frames;
+
+ cd += (timed ? 4 : 3);
+ }
+ }
+ cd = cdata;
+ pd = pdata;
+ for (i = 0, point = edit->points; i < totpoint; i++, point++) {
+ if (point->flag & PEP_HIDE || point->totkey == 0)
+ continue;
+
+ if (point->keys->flag & PEK_USE_WCO)
+ glVertexPointer(3, GL_FLOAT, sizeof(PTCacheEditKey), point->keys->world_co);
+ else
+ glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), pd);
+
+ glColorPointer((timed ? 4 : 3), GL_FLOAT, (timed ? 4 : 3) * sizeof(float), cd);
+
+ glDrawArrays(GL_POINTS, 0, point->totkey);
+
+ pd += pd ? 3 * point->totkey : 0;
+ cd += (timed ? 4 : 3) * point->totkey;
+ }
+ if (pdata) { MEM_freeN(pdata); pd = pdata = NULL; }
+ if (cdata) { MEM_freeN(cdata); cd = cdata = NULL; }
+ }
+ else if (pset->selectmode == SCE_SELECT_END) {
+ glBegin(GL_POINTS);
+ for (i = 0, point = edit->points; i < totpoint; i++, point++) {
+ if ((point->flag & PEP_HIDE) == 0 && point->totkey) {
+ key = point->keys + point->totkey - 1;
+ glColor3fv((key->flag & PEK_SELECT) ? sel_col : nosel_col);
+ /* has to be like this.. otherwise selection won't work, have try glArrayElement later..*/
+ glVertex3fv((key->flag & PEK_USE_WCO) ? key->world_co : key->co);
+ }
+ }
+ glEnd();
+ }
+ }
+
+ glDisable(GL_BLEND);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
+}
static void ob_draw_RE_motion(float com[3], float rotscale[3][3], float itw, float ith, float drw_size)
{
@@ -7459,12 +8573,15 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
Object *ob = base->object;
Curve *cu;
RegionView3D *rv3d = ar->regiondata;
+ unsigned int col = 0;
unsigned char _ob_wire_col[4]; /* dont initialize this */
const unsigned char *ob_wire_col = NULL; /* dont initialize this, use NULL crashes as a way to find invalid use */
bool zbufoff = false, is_paint = false, empty_object = false;
const bool is_obact = (ob == OBACT);
const bool render_override = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0;
const bool is_picking = (G.f & G_PICKSEL) != 0;
+ const bool has_particles = (ob->particlesystem.first != NULL);
+ bool skip_object = false; /* Draw particles but not their emitter object. */
SmokeModifierData *smd = NULL;
if (ob != scene->obedit) {
@@ -7475,11 +8592,31 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
if (ob->restrictflag & OB_RESTRICT_RENDER)
return;
- if (ob->transflag & (OB_DUPLI & ~OB_DUPLIFRAMES))
+ if (!has_particles && (ob->transflag & (OB_DUPLI & ~OB_DUPLIFRAMES)))
return;
}
}
+ if (has_particles) {
+ /* XXX particles are not safe for simultaneous threaded render */
+ if (G.is_rendering) {
+ return;
+ }
+
+ if (ob->mode == OB_MODE_OBJECT) {
+ ParticleSystem *psys;
+
+ skip_object = render_override;
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ /* Once we have found a psys which renders its emitter object, we are done. */
+ if (psys->part->draw & PART_DRAW_EMITTER) {
+ skip_object = false;
+ break;
+ }
+ }
+ }
+ }
+
if (((base->flag & OB_FROMDUPLI) == 0) &&
(md = modifiers_findByType(ob, eModifierType_Smoke)) &&
(modifier_isEnabled(scene, md, eModifierMode_Realtime)))
@@ -7505,8 +8642,8 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
/* xray delay? */
if ((dflag & DRAW_PICKING) == 0 && (base->flag & OB_FROMDUPLI) == 0 && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) {
- /* sync with master */
- {
+ /* don't do xray in particle mode, need the z-buffer */
+ if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
/* xray and transp are set when it is drawing the 2nd/3rd pass */
if (!v3d->xray && !v3d->transp && (ob->dtx & OB_DRAWXRAY) && !(ob->dtx & OB_DRAWTRANSP)) {
ED_view3d_after_add(&v3d->afterdraw_xray, base, dflag);
@@ -7608,8 +8745,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
}
}
- /* sync with master */
- {
+ if (!skip_object) {
/* draw outline for selected objects, mesh does itself */
if ((v3d->flag & V3D_SELECT_OUTLINE) && !render_override && ob->type != OB_MESH) {
if (dt > OB_WIRE && (ob->mode & OB_MODE_EDIT) == 0 && (dflag & DRAW_SCENESET) == 0) {
@@ -7758,6 +8894,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
if (!render_override) {
drawaxes(rv3d->viewmatob, 1.0, OB_ARROWS, ob_wire_col);
}
+ break;
}
/* TODO Viewport: some elements are being drawn for object selection only */
@@ -7787,10 +8924,63 @@ afterdraw:
}
}
+ /* code for new particle system */
+ if ((ob->particlesystem.first) &&
+ (ob != scene->obedit))
+ {
+ ParticleSystem *psys;
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+ /* for visibility, also while wpaint */
+ if (col || (ob->flag & SELECT)) {
+ cpack(0xFFFFFF);
+ }
+ }
+ //glDepthMask(GL_FALSE);
+
+ glLoadMatrixf(rv3d->viewmat);
+
+ view3d_cached_text_draw_begin();
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ /* run this so that possible child particles get cached */
+ if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) {
+ PTCacheEdit *edit = PE_create_current(scene, ob);
+ if (edit && edit->psys == psys)
+ draw_update_ptcache_edit(scene, ob, edit);
+ }
+
+ draw_new_particle_system(scene, v3d, rv3d, base, psys, dt, dflag);
+ }
+ invert_m4_m4(ob->imat, ob->obmat);
+ view3d_cached_text_draw_end(v3d, ar, 0, NULL);
+
+ glMultMatrixf(ob->obmat);
+
+ //glDepthMask(GL_TRUE);
+ if (col) cpack(col);
+ }
+
+ /* draw edit particles last so that they can draw over child particles */
+ if ((dflag & DRAW_PICKING) == 0 &&
+ (!scene->obedit))
+ {
+
+ if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) {
+ PTCacheEdit *edit = PE_create_current(scene, ob);
+ if (edit) {
+ glLoadMatrixf(rv3d->viewmat);
+ draw_update_ptcache_edit(scene, ob, edit);
+ draw_ptcache_edit(scene, v3d, edit);
+ glMultMatrixf(ob->obmat);
+ }
+ }
+ }
+
/* draw code for smoke, only draw domains */
if (smd && smd->domain) {
SmokeDomainSettings *sds = smd->domain;
- const bool show_smoke = true; /* XXX was checking cached frame range before */
+ const bool show_smoke = (CFRA >= sds->point_cache[0]->startframe);
float viewnormal[3];
glLoadMatrixf(rv3d->viewmat);
diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c
index 584f442bd44..27ecbf83db5 100644
--- a/source/blender/editors/space_view3d/drawvolume.c
+++ b/source/blender/editors/space_view3d/drawvolume.c
@@ -32,7 +32,6 @@
#include "MEM_guardedalloc.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_smoke_types.h"
@@ -43,6 +42,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_texture.h"
+#include "BKE_particle.h"
#include "smoke_API.h"
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 4526d120923..90fa54c7a16 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -878,6 +878,7 @@ static void view3d_main_region_listener(bScreen *sc, ScrArea *sa, ARegion *ar, w
case ND_MODIFIER:
case ND_CONSTRAINT:
case ND_KEYS:
+ case ND_PARTICLE:
case ND_LOD:
ED_region_tag_redraw(ar);
break;
diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c
index 7193ce1b3cd..639be36d739 100644
--- a/source/blender/editors/space_view3d/view3d_draw_legacy.c
+++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c
@@ -429,6 +429,11 @@ static void backdrawview3d(Scene *scene, wmWindow *win, ARegion *ar, View3D *v3d
{
/* do nothing */
}
+ else if ((base && (base->object->mode & OB_MODE_PARTICLE_EDIT)) &&
+ V3D_IS_ZBUF(v3d))
+ {
+ /* do nothing */
+ }
else if (scene->obedit &&
V3D_IS_ZBUF(v3d))
{
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 6f45013ce42..620bbf03f32 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -71,6 +71,7 @@
#include "RNA_define.h"
#include "ED_armature.h"
+#include "ED_particle.h"
#include "ED_keyframing.h"
#include "ED_screen.h"
#include "ED_transform.h"
@@ -3078,6 +3079,9 @@ static int viewselected_exec(bContext *C, wmOperator *op)
else if (BKE_paint_select_face_test(ob)) {
ok = paintface_minmax(ob, min, max);
}
+ else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
+ ok = PE_minmax(scene, min, max);
+ }
else if (ob &&
(ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
{
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index 32e265cb981..ab0cae6822b 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -347,7 +347,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
}
/* Manipulators aren't used in paint modes */
- if (ob->mode != OB_MODE_SCULPT) {
+ if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) {
/* masks aren't used for sculpt and particle painting */
PointerRNA meshptr;
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 73af8377d41..504a8383a41 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -280,7 +280,7 @@ void ED_view3d_cameracontrol_update(
void ED_view3d_cameracontrol_release(
struct View3DCameraControl *vctrl,
const bool restore);
-struct Object *ED_view3d_cameracontrol_object_get(
+Object *ED_view3d_cameracontrol_object_get(
struct View3DCameraControl *vctrl);
/* view3d_toolbar.c */
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index f77e836461c..3239d07553f 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -85,6 +85,7 @@
#include "ED_armature.h"
#include "ED_curve.h"
+#include "ED_particle.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_screen.h"
@@ -835,6 +836,8 @@ static void view3d_lasso_select(bContext *C, ViewContext *vc,
else if (ob && (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
/* pass */
}
+ else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT))
+ PE_lasso_select(C, mcords, moves, extend, select);
else {
do_lasso_select_objects(vc, mcords, moves, extend, select);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
@@ -2170,6 +2173,9 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op)
else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
ret = do_paintvert_box_select(&vc, &rect, select, extend);
}
+ else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
+ ret = PE_border_select(C, &rect, select, extend);
+ }
else { /* object mode with none active */
ret = do_object_pose_box_select(C, &vc, &rect, select, extend);
}
@@ -2300,6 +2306,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle);
}
+ else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT)
+ return PE_mouse_particles(C, location, extend, deselect, toggle);
else if (obact && BKE_paint_select_face_test(obact))
retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
else if (BKE_paint_select_vert_test(obact))
@@ -2815,7 +2823,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
RNA_int_get(op->ptr, "y")};
if (CTX_data_edit_object(C) || BKE_paint_select_elem_test(obact) ||
- (obact && (obact->mode & OB_MODE_POSE)) )
+ (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) )
{
ViewContext vc;
@@ -2837,6 +2845,8 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
}
else if (obact->mode & OB_MODE_POSE)
pose_circle_select(&vc, select, mval, (float)radius);
+ else
+ return PE_circle_select(C, select, mval, (float)radius);
}
else if (obact && obact->mode & OB_MODE_SCULPT) {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 7be17c04670..20c62e91d01 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -42,7 +42,6 @@
#include "DNA_constraint_types.h"
#include "DNA_mask_types.h"
#include "DNA_movieclip_types.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h" /* PET modes */
#include "BLI_alloca.h"
@@ -60,6 +59,7 @@
#include "BKE_editmesh_bvh.h"
#include "BKE_context.h"
#include "BKE_constraint.h"
+#include "BKE_particle.h"
#include "BKE_unit.h"
#include "BKE_mask.h"
#include "BKE_report.h"
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 22d6e7af7fe..a59f9dc43dd 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -624,6 +624,7 @@ void flushTransIntFrameActionData(TransInfo *t);
void flushTransGraphData(TransInfo *t);
void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data);
void flushTransUVs(TransInfo *t);
+void flushTransParticles(TransInfo *t);
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
void flushTransNodes(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index f5b5faa4ad2..ce3d903b8f6 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -50,7 +50,6 @@
#include "DNA_gpencil_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
-#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
@@ -82,7 +81,9 @@
#include "BKE_nla.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_paint.h"
+#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_rigidbody.h"
#include "BKE_scene.h"
@@ -95,6 +96,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
+#include "ED_particle.h"
#include "ED_image.h"
#include "ED_keyframing.h"
#include "ED_keyframes_edit.h"
@@ -1801,6 +1803,174 @@ static void createTransLatticeVerts(TransInfo *t)
}
}
+/* ******************* particle edit **************** */
+static void createTransParticleVerts(bContext *C, TransInfo *t)
+{
+ TransData *td = NULL;
+ TransDataExtension *tx;
+ Base *base = CTX_data_active_base(C);
+ Object *ob = CTX_data_active_object(C);
+ ParticleEditSettings *pset = PE_settings(t->scene);
+ PTCacheEdit *edit = PE_get_current(t->scene, ob);
+ ParticleSystem *psys = NULL;
+ ParticleSystemModifierData *psmd = NULL;
+ PTCacheEditPoint *point;
+ PTCacheEditKey *key;
+ float mat[4][4];
+ int i, k, transformparticle;
+ int count = 0, hasselected = 0;
+ const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+
+ if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) return;
+
+ psys = edit->psys;
+
+ if (psys)
+ psmd = psys_get_modifier(ob, psys);
+
+ base->flag |= BA_HAS_RECALC_DATA;
+
+ for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) {
+ point->flag &= ~PEP_TRANSFORM;
+ transformparticle = 0;
+
+ if ((point->flag & PEP_HIDE) == 0) {
+ for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
+ if ((key->flag & PEK_HIDE) == 0) {
+ if (key->flag & PEK_SELECT) {
+ hasselected = 1;
+ transformparticle = 1;
+ }
+ else if (is_prop_edit)
+ transformparticle = 1;
+ }
+ }
+ }
+
+ if (transformparticle) {
+ count += point->totkey;
+ point->flag |= PEP_TRANSFORM;
+ }
+ }
+
+ /* note: in prop mode we need at least 1 selected */
+ if (hasselected == 0) return;
+
+ t->total = count;
+ td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Particle Mode)");
+
+ if (t->mode == TFM_BAKE_TIME)
+ tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "Particle_TransExtension");
+ else
+ tx = t->ext = NULL;
+
+ unit_m4(mat);
+
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) {
+ TransData *head, *tail;
+ head = tail = td;
+
+ if (!(point->flag & PEP_TRANSFORM)) continue;
+
+ if (psys && !(psys->flag & PSYS_GLOBAL_HAIR))
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + i, mat);
+
+ for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
+ if (key->flag & PEK_USE_WCO) {
+ copy_v3_v3(key->world_co, key->co);
+ mul_m4_v3(mat, key->world_co);
+ td->loc = key->world_co;
+ }
+ else
+ td->loc = key->co;
+
+ copy_v3_v3(td->iloc, td->loc);
+ copy_v3_v3(td->center, td->loc);
+
+ if (key->flag & PEK_SELECT)
+ td->flag |= TD_SELECTED;
+ else if (!is_prop_edit)
+ td->flag |= TD_SKIP;
+
+ unit_m3(td->mtx);
+ unit_m3(td->smtx);
+
+ /* don't allow moving roots */
+ if (k == 0 && pset->flag & PE_LOCK_FIRST && (!psys || !(psys->flag & PSYS_GLOBAL_HAIR)))
+ td->protectflag |= OB_LOCK_LOC;
+
+ td->ob = ob;
+ td->ext = tx;
+ if (t->mode == TFM_BAKE_TIME) {
+ td->val = key->time;
+ td->ival = *(key->time);
+ /* abuse size and quat for min/max values */
+ td->flag |= TD_NO_EXT;
+ if (k == 0) tx->size = NULL;
+ else tx->size = (key - 1)->time;
+
+ if (k == point->totkey - 1) tx->quat = NULL;
+ else tx->quat = (key + 1)->time;
+ }
+
+ td++;
+ if (tx)
+ tx++;
+ tail++;
+ }
+ if (is_prop_edit && head != tail)
+ calc_distanceCurveVerts(head, tail - 1);
+ }
+}
+
+void flushTransParticles(TransInfo *t)
+{
+ Scene *scene = t->scene;
+ Object *ob = OBACT;
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ ParticleSystem *psys = edit->psys;
+ ParticleSystemModifierData *psmd = NULL;
+ PTCacheEditPoint *point;
+ PTCacheEditKey *key;
+ TransData *td;
+ float mat[4][4], imat[4][4], co[3];
+ int i, k;
+ const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+
+ if (psys)
+ psmd = psys_get_modifier(ob, psys);
+
+ /* we do transform in world space, so flush world space position
+ * back to particle local space (only for hair particles) */
+ td = t->data;
+ for (i = 0, point = edit->points; i < edit->totpoint; i++, point++, td++) {
+ if (!(point->flag & PEP_TRANSFORM)) continue;
+
+ if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
+ psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + i, mat);
+ invert_m4_m4(imat, mat);
+
+ for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
+ copy_v3_v3(co, key->world_co);
+ mul_m4_v3(imat, co);
+
+
+ /* optimization for proportional edit */
+ if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) {
+ copy_v3_v3(key->co, co);
+ point->flag |= PEP_EDIT_RECALC;
+ }
+ }
+ }
+ else
+ point->flag |= PEP_EDIT_RECALC;
+ }
+
+ PE_update_object(scene, OBACT, 1);
+}
+
/* ********************* mesh ****************** */
static bool bmesh_test_dist_add(BMVert *v, BMVert *v_other,
@@ -6145,6 +6315,13 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
else if (t->options & CTX_PAINT_CURVE) {
/* pass */
}
+ else if ((t->scene->basact) &&
+ (ob = t->scene->basact->object) &&
+ (ob->mode & OB_MODE_PARTICLE_EDIT) &&
+ PE_get_current(t->scene, ob))
+ {
+ /* do nothing */
+ }
else { /* Objects */
int i;
@@ -6152,6 +6329,8 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
for (i = 0; i < t->total; i++) {
TransData *td = t->data + i;
+ ListBase pidlist;
+ PTCacheID *pid;
ob = td->ob;
if (td->flag & TD_NOACTION)
@@ -6160,6 +6339,18 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
if (td->flag & TD_SKIP)
continue;
+ /* flag object caches as outdated */
+ BKE_ptcache_ids_from_object(&pidlist, ob, t->scene, MAX_DUPLI_RECUR);
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->type != PTCACHE_TYPE_PARTICLES) /* particles don't need reset on geometry change */
+ pid->cache->flag |= PTCACHE_OUTDATED;
+ }
+ BLI_freelistN(&pidlist);
+
+ /* pointcache refresh */
+ if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED))
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
/* Needed for proper updating of "quick cached" dynamics. */
/* Creates troubles for moving animated objects without */
/* autokey though, probably needed is an anim sys override? */
@@ -7879,6 +8070,16 @@ void createTransData(bContext *C, TransInfo *t)
}
}
+ else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_start_edit(PE_get_current(scene, ob))) {
+ createTransParticleVerts(C, t);
+ t->flag |= T_POINTS;
+
+ if (t->data && t->flag & T_PROP_EDIT) {
+ sort_trans_data(t); // makes selected become first in array
+ set_prop_dist(t, 1);
+ sort_trans_data_dist(t);
+ }
+ }
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
t->flag |= T_POINTS | T_2D_EDIT;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 9e9372c72ea..f78a23be7b8 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -91,6 +91,7 @@
#include "ED_markers.h"
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_particle.h"
#include "ED_screen_types.h"
#include "ED_space_api.h"
#include "ED_uvedit.h"
@@ -708,6 +709,8 @@ static void recalcData_spaceclip(TransInfo *t)
/* helper for recalcData() - for object transforms, typically in the 3D view */
static void recalcData_objects(TransInfo *t)
{
+ Base *base = t->scene->basact;
+
if (t->obedit) {
if (ELEM(t->obedit->type, OB_CURVE, OB_SURF)) {
Curve *cu = t->obedit->data;
@@ -893,6 +896,12 @@ static void recalcData_objects(TransInfo *t)
else
BKE_pose_where_is(t->scene, ob);
}
+ else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, base->object)) {
+ if (t->state != TRANS_CANCEL) {
+ applyProject(t);
+ }
+ flushTransParticles(t);
+ }
else {
int i;
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c
index f6fa464bb93..e1abf34b0f4 100644
--- a/source/blender/editors/transform/transform_manipulator.c
+++ b/source/blender/editors/transform/transform_manipulator.c
@@ -40,7 +40,6 @@
#include "DNA_gpencil_types.h"
#include "DNA_lattice_types.h"
#include "DNA_meta_types.h"
-#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
@@ -55,6 +54,8 @@
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_global.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
#include "BKE_gpencil.h"
@@ -66,6 +67,7 @@
#include "ED_armature.h"
#include "ED_curve.h"
+#include "ED_particle.h"
#include "ED_view3d.h"
#include "ED_gpencil.h"
@@ -558,6 +560,30 @@ static int calc_manipulator_stats(const bContext *C)
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
/* pass */
}
+ else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ PTCacheEditPoint *point;
+ PTCacheEditKey *ek;
+ int k;
+
+ if (edit) {
+ point = edit->points;
+ for (a = 0; a < edit->totpoint; a++, point++) {
+ if (point->flag & PEP_HIDE) continue;
+
+ for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
+ if (ek->flag & PEK_SELECT) {
+ calc_tw_center(scene, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
+ totsel++;
+ }
+ }
+ }
+
+ /* selection center */
+ if (totsel)
+ mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
+ }
+ }
else {
/* we need the one selected object, if its not active */
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 1d6a392aae6..90a4aa3614d 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -1011,7 +1011,7 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3
result = ORIENTATION_EDGE;
}
}
- else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
+ else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
/* pass */
}
else {
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 121a23a7027..f8bb124e943 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -519,6 +519,8 @@ static void initSnappingMode(TransInfo *t)
{
ToolSettings *ts = t->settings;
Object *obedit = t->obedit;
+ Scene *scene = t->scene;
+ Base *base_act = scene->basact;
if (t->spacetype == SPACE_NODE) {
/* force project off when not supported */
@@ -557,6 +559,12 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.modeSelect = t->tsnap.snap_self ? SNAP_ALL : SNAP_NOT_ACTIVE;
}
}
+ /* Particles edit mode*/
+ else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
+ (obedit == NULL && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT))
+ {
+ t->tsnap.modeSelect = SNAP_ALL;
+ }
/* Object mode */
else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
(obedit == NULL) ) // Object Mode
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index c8ccb3772c5..02900d7022c 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -1867,7 +1867,23 @@ static bool snapObjectsRay(
unsigned int ob_index = 0;
Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL;
+
+ /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA
+ * which makes the loop skip it, even the derived mesh will never change
+ *
+ * To solve that problem, we do it first as an exception.
+ * */
Base *base_act = sctx->scene->basact;
+ if (base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT) {
+ Object *ob = base_act->object;
+
+ retval |= snapObject(
+ sctx, ob, ob->obmat, ob_index++,
+ false, snap_to, mval,
+ ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
+ r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
+ }
bool ignore_object_selected = false, ignore_object_active = false;
switch (snap_select) {
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 321b1043595..c0b30f93939 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -72,6 +72,7 @@ set(SRC
../include/ED_object.h
../include/ED_outliner.h
../include/ED_paint.h
+ ../include/ED_particle.h
../include/ED_physics.h
../include/ED_render.h
../include/ED_screen.h
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index 7fd67849414..4a9311416b3 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -49,6 +49,7 @@
#include "BKE_screen.h"
#include "ED_armature.h"
+#include "ED_particle.h"
#include "ED_curve.h"
#include "ED_gpencil.h"
#include "ED_mball.h"
@@ -97,6 +98,11 @@ void ED_undo_push(bContext *C, const char *str)
else if (obedit->type == OB_ARMATURE)
undo_push_armature(C, str);
}
+ else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
+ if (U.undosteps == 0) return;
+
+ PE_undo_push(CTX_data_scene(C), str);
+ }
else if (obact && obact->mode & OB_MODE_SCULPT) {
/* do nothing for now */
}
@@ -172,6 +178,12 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
else if (obact && obact->mode & OB_MODE_SCULPT) {
ED_undo_paint_step(C, UNDO_PAINT_MESH, step, undoname);
}
+ else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
+ if (step == 1)
+ PE_undo(scene);
+ else
+ PE_redo(scene);
+ }
else if (U.uiflag & USER_GLOBALUNDO) {
// note python defines not valid here anymore.
//#ifdef WITH_PYTHON
@@ -284,6 +296,9 @@ bool ED_undo_is_valid(const bContext *C, const char *undoname)
if (ED_undo_paint_is_valid(UNDO_PAINT_MESH, undoname))
return 1;
}
+ else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
+ return PE_undo_is_valid(CTX_data_scene(C));
+ }
if (U.uiflag & USER_GLOBALUNDO) {
return BKE_undo_is_valid(undoname);
@@ -443,8 +458,9 @@ void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_ev
enum {
UNDOSYSTEM_GLOBAL = 1,
UNDOSYSTEM_EDITMODE = 2,
- UNDOSYSTEM_IMAPAINT = 3,
- UNDOSYSTEM_SCULPT = 4,
+ UNDOSYSTEM_PARTICLE = 3,
+ UNDOSYSTEM_IMAPAINT = 4,
+ UNDOSYSTEM_SCULPT = 5,
};
static int get_undo_system(bContext *C)
@@ -470,7 +486,9 @@ static int get_undo_system(bContext *C)
}
else {
if (obact) {
- if (obact->mode & OB_MODE_TEXTURE_PAINT) {
+ if (obact->mode & OB_MODE_PARTICLE_EDIT)
+ return UNDOSYSTEM_PARTICLE;
+ else if (obact->mode & OB_MODE_TEXTURE_PAINT) {
if (!ED_undo_paint_empty(UNDO_PAINT_IMAGE))
return UNDOSYSTEM_IMAPAINT;
}
@@ -496,7 +514,10 @@ static EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem)
while (true) {
const char *name = NULL;
- if (undosys == UNDOSYSTEM_EDITMODE) {
+ if (undosys == UNDOSYSTEM_PARTICLE) {
+ name = PE_undo_get_name(CTX_data_scene(C), i, &active);
+ }
+ else if (undosys == UNDOSYSTEM_EDITMODE) {
name = undo_editmode_get_name(C, i, &active);
}
else if (undosys == UNDOSYSTEM_IMAPAINT) {
@@ -576,7 +597,10 @@ static int undo_history_exec(bContext *C, wmOperator *op)
int undosys = get_undo_system(C);
int item = RNA_int_get(op->ptr, "item");
- if (undosys == UNDOSYSTEM_EDITMODE) {
+ if (undosys == UNDOSYSTEM_PARTICLE) {
+ PE_undo_number(CTX_data_scene(C), item);
+ }
+ else if (undosys == UNDOSYSTEM_EDITMODE) {
undo_editmode_number(C, item + 1);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
}
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index 202e5fd3ad7..0d92d22a173 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -62,6 +62,7 @@ typedef struct GPUNode GPUNode;
typedef struct GPUNodeLink GPUNodeLink;
typedef struct GPUMaterial GPUMaterial;
typedef struct GPULamp GPULamp;
+typedef struct GPUParticleInfo GPUParticleInfo;
/* Functions to create GPU Materials nodes */
@@ -91,7 +92,11 @@ typedef enum GPUBuiltin {
GPU_OBCOLOR = (1 << 6),
GPU_AUTO_BUMPSCALE = (1 << 7),
GPU_CAMERA_TEXCO_FACTORS = (1 << 8),
- GPU_LOC_TO_VIEW_MATRIX = (1 << 9),
+ GPU_PARTICLE_SCALAR_PROPS = (1 << 9),
+ GPU_PARTICLE_LOCATION = (1 << 10),
+ GPU_PARTICLE_VELOCITY = (1 << 11),
+ GPU_PARTICLE_ANG_VELOCITY = (1 << 12),
+ GPU_LOC_TO_VIEW_MATRIX = (1 << 13),
GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14),
} GPUBuiltin;
@@ -225,7 +230,7 @@ void GPU_material_bind(
float viewmat[4][4], float viewinv[4][4], float cameraborder[4], bool scenelock);
void GPU_material_bind_uniforms(
GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4],
- float autobumpscale);
+ float autobumpscale, GPUParticleInfo *pi);
void GPU_material_unbind(GPUMaterial *material);
bool GPU_material_bound(GPUMaterial *material);
struct Scene *GPU_material_scene(GPUMaterial *material);
@@ -334,6 +339,14 @@ void GPU_horizon_update_color(float color[3]);
void GPU_ambient_update_color(float color[3]);
void GPU_zenith_update_color(float color[3]);
+struct GPUParticleInfo
+{
+ float scalprops[4];
+ float location[3];
+ float velocity[3];
+ float angular_velocity[3];
+};
+
#ifdef WITH_OPENSUBDIV
struct DerivedMesh;
void GPU_material_update_fvar_offset(GPUMaterial *gpu_material,
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index f33f5157f56..211394e7932 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -402,6 +402,14 @@ const char *GPU_builtin_name(GPUBuiltin builtin)
return "unfobautobumpscale";
else if (builtin == GPU_CAMERA_TEXCO_FACTORS)
return "unfcameratexfactors";
+ else if (builtin == GPU_PARTICLE_SCALAR_PROPS)
+ return "unfparticlescalarprops";
+ else if (builtin == GPU_PARTICLE_LOCATION)
+ return "unfparticleco";
+ else if (builtin == GPU_PARTICLE_VELOCITY)
+ return "unfparticlevel";
+ else if (builtin == GPU_PARTICLE_ANG_VELOCITY)
+ return "unfparticleangvel";
else
return "";
}
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index 49faaef23e1..c8d5d92b66b 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -56,6 +56,7 @@
#include "DNA_scene_types.h"
#include "DNA_smoke_types.h"
#include "DNA_view3d_types.h"
+#include "DNA_particle_types.h"
#include "MEM_guardedalloc.h"
@@ -1871,6 +1872,35 @@ void GPU_begin_object_materials(
GPU_object_material_unbind();
}
+static int GPU_get_particle_info(GPUParticleInfo *pi)
+{
+ DupliObject *dob = GMS.dob;
+ if (dob->particle_system) {
+ int ind;
+ if (dob->persistent_id[0] < dob->particle_system->totpart)
+ ind = dob->persistent_id[0];
+ else {
+ ind = dob->particle_system->child[dob->persistent_id[0] - dob->particle_system->totpart].parent;
+ }
+ if (ind >= 0) {
+ ParticleData *p = &dob->particle_system->particles[ind];
+
+ pi->scalprops[0] = ind;
+ pi->scalprops[1] = GMS.gscene->r.cfra - p->time;
+ pi->scalprops[2] = p->lifetime;
+ pi->scalprops[3] = p->size;
+
+ copy_v3_v3(pi->location, p->state.co);
+ copy_v3_v3(pi->velocity, p->state.vel);
+ copy_v3_v3(pi->angular_velocity, p->state.ave);
+ return 1;
+ }
+ else return 0;
+ }
+ else
+ return 0;
+}
+
int GPU_object_material_bind(int nr, void *attribs)
{
GPUVertexAttribs *gattribs = attribs;
@@ -1929,18 +1959,22 @@ int GPU_object_material_bind(int nr, void *attribs)
if (gattribs && GMS.gmatbuf[nr]) {
/* bind glsl material and get attributes */
Material *mat = GMS.gmatbuf[nr];
+ GPUParticleInfo partile_info;
float auto_bump_scale;
GPUMaterial *gpumat = GPU_material_from_blender(GMS.gscene, mat, GMS.is_opensubdiv);
GPU_material_vertex_attributes(gpumat, gattribs);
+ if (GMS.dob)
+ GPU_get_particle_info(&partile_info);
+
GPU_material_bind(
gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT),
GMS.gviewmat, GMS.gviewinv, GMS.gviewcamtexcofac, GMS.gscenelock);
auto_bump_scale = GMS.gob->derivedFinal != NULL ? GMS.gob->derivedFinal->auto_bump_scale : 1.0f;
- GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale);
+ GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale, &partile_info);
GMS.gboundmat = mat;
/* for glsl use alpha blend mode, unless it's set to solid and
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 3e8f0baf7b0..56b7af787e7 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -117,6 +117,11 @@ struct GPUMaterial {
int obcolloc, obautobumpscaleloc;
int cameratexcofacloc;
+ int partscalarpropsloc;
+ int partcoloc;
+ int partvel;
+ int partangvel;
+
ListBase lamps;
bool bound;
@@ -255,6 +260,14 @@ static int GPU_material_construct_end(GPUMaterial *material, const char *passnam
material->obautobumpscaleloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_AUTO_BUMPSCALE));
if (material->builtins & GPU_CAMERA_TEXCO_FACTORS)
material->cameratexcofacloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_CAMERA_TEXCO_FACTORS));
+ if (material->builtins & GPU_PARTICLE_SCALAR_PROPS)
+ material->partscalarpropsloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_SCALAR_PROPS));
+ if (material->builtins & GPU_PARTICLE_LOCATION)
+ material->partcoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_LOCATION));
+ if (material->builtins & GPU_PARTICLE_VELOCITY)
+ material->partvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_VELOCITY));
+ if (material->builtins & GPU_PARTICLE_ANG_VELOCITY)
+ material->partangvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_ANG_VELOCITY));
return 1;
}
else {
@@ -387,7 +400,7 @@ void GPU_material_bind(
void GPU_material_bind_uniforms(
GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4],
- float autobumpscale)
+ float autobumpscale, GPUParticleInfo *pi)
{
if (material->pass) {
GPUShader *shader = GPU_pass_shader(material->pass);
@@ -424,6 +437,18 @@ void GPU_material_bind_uniforms(
if (material->builtins & GPU_AUTO_BUMPSCALE) {
GPU_shader_uniform_vector(shader, material->obautobumpscaleloc, 1, 1, &autobumpscale);
}
+ if (material->builtins & GPU_PARTICLE_SCALAR_PROPS) {
+ GPU_shader_uniform_vector(shader, material->partscalarpropsloc, 4, 1, pi->scalprops);
+ }
+ if (material->builtins & GPU_PARTICLE_LOCATION) {
+ GPU_shader_uniform_vector(shader, material->partcoloc, 3, 1, pi->location);
+ }
+ if (material->builtins & GPU_PARTICLE_VELOCITY) {
+ GPU_shader_uniform_vector(shader, material->partvel, 3, 1, pi->velocity);
+ }
+ if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) {
+ GPU_shader_uniform_vector(shader, material->partangvel, 3, 1, pi->angular_velocity);
+ }
}
}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index f5c714a7629..ed719b66eb3 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -247,6 +247,7 @@ typedef enum ID_Type {
ID_AC = MAKE_ID2('A', 'C'), /* bAction */
ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */
ID_BR = MAKE_ID2('B', 'R'), /* Brush */
+ ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */
ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */
ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */
ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */
@@ -385,7 +386,8 @@ enum {
FILTER_ID_TXT = (1 << 24),
FILTER_ID_VF = (1 << 25),
FILTER_ID_WO = (1 << 26),
- FILTER_ID_CF = (1 << 27),
+ FILTER_ID_PA = (1 << 27),
+ FILTER_ID_CF = (1 << 28),
};
/* IMPORTANT: this enum matches the order currently use in set_lisbasepointers,
@@ -415,6 +417,7 @@ enum {
INDEX_ID_PAL,
INDEX_ID_PC,
INDEX_ID_BR,
+ INDEX_ID_PA,
INDEX_ID_SPK,
INDEX_ID_WO,
INDEX_ID_MC,
diff --git a/source/blender/makesdna/DNA_boid_types.h b/source/blender/makesdna/DNA_boid_types.h
new file mode 100644
index 00000000000..f1930ffd643
--- /dev/null
+++ b/source/blender/makesdna/DNA_boid_types.h
@@ -0,0 +1,225 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_boid_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_BOID_TYPES_H__
+#define __DNA_BOID_TYPES_H__
+
+#include "DNA_listBase.h"
+
+typedef enum BoidRuleType {
+ eBoidRuleType_None = 0,
+ eBoidRuleType_Goal = 1, /* go to goal assigned object or loudest assigned signal source */
+ eBoidRuleType_Avoid = 2, /* get away from assigned object or loudest assigned signal source */
+ eBoidRuleType_AvoidCollision = 3, /* manoeuver to avoid collisions with other boids and deflector object in near future */
+ eBoidRuleType_Separate = 4, /* keep from going through other boids */
+ eBoidRuleType_Flock = 5, /* move to center of neighbors and match their velocity */
+ eBoidRuleType_FollowLeader = 6, /* follow a boid or assigned object */
+ eBoidRuleType_AverageSpeed = 7, /* maintain speed, flight level or wander*/
+ eBoidRuleType_Fight = 8, /* go to closest enemy and attack when in range */
+ //eBoidRuleType_Protect = 9, /* go to enemy closest to target and attack when in range */
+ //eBoidRuleType_Hide = 10, /* find a deflector move to it's other side from closest enemy */
+ //eBoidRuleType_FollowPath = 11, /* move along a assigned curve or closest curve in a group */
+ //eBoidRuleType_FollowWall = 12, /* move next to a deflector object's in direction of it's tangent */
+ NUM_BOID_RULE_TYPES
+} BoidRuleType;
+
+/* boidrule->flag */
+#define BOIDRULE_CURRENT 1
+#define BOIDRULE_IN_AIR 4
+#define BOIDRULE_ON_LAND 8
+typedef struct BoidRule {
+ struct BoidRule *next, *prev;
+ int type, flag;
+ char name[32];
+} BoidRule;
+#define BRULE_GOAL_AVOID_PREDICT 1
+#define BRULE_GOAL_AVOID_ARRIVE 2
+#define BRULE_GOAL_AVOID_SIGNAL 4
+typedef struct BoidRuleGoalAvoid {
+ BoidRule rule;
+ struct Object *ob;
+ int options;
+ float fear_factor;
+
+ /* signals */
+ int signal_id, channels;
+} BoidRuleGoalAvoid;
+#define BRULE_ACOLL_WITH_BOIDS 1
+#define BRULE_ACOLL_WITH_DEFLECTORS 2
+typedef struct BoidRuleAvoidCollision {
+ BoidRule rule;
+ int options;
+ float look_ahead;
+} BoidRuleAvoidCollision;
+#define BRULE_LEADER_IN_LINE 1
+typedef struct BoidRuleFollowLeader {
+ BoidRule rule;
+ struct Object *ob;
+ float loc[3], oloc[3];
+ float cfra, distance;
+ int options, queue_size;
+} BoidRuleFollowLeader;
+typedef struct BoidRuleAverageSpeed {
+ BoidRule rule;
+ float wander, level, speed, rt;
+} BoidRuleAverageSpeed;
+typedef struct BoidRuleFight {
+ BoidRule rule;
+ float distance, flee_distance;
+} BoidRuleFight;
+
+typedef enum BoidMode {
+ eBoidMode_InAir = 0,
+ eBoidMode_OnLand = 1,
+ eBoidMode_Climbing = 2,
+ eBoidMode_Falling = 3,
+ eBoidMode_Liftoff = 4,
+ NUM_BOID_MODES
+} BoidMode;
+
+
+typedef struct BoidData {
+ float health, acc[3];
+ short state_id, mode;
+} BoidData;
+
+// planned for near future
+//typedef enum BoidConditionMode {
+// eBoidConditionType_Then = 0,
+// eBoidConditionType_And = 1,
+// eBoidConditionType_Or = 2,
+// NUM_BOID_CONDITION_MODES
+//} BoidConditionMode;
+//typedef enum BoidConditionType {
+// eBoidConditionType_None = 0,
+// eBoidConditionType_Signal = 1,
+// eBoidConditionType_NoSignal = 2,
+// eBoidConditionType_HealthBelow = 3,
+// eBoidConditionType_HealthAbove = 4,
+// eBoidConditionType_See = 5,
+// eBoidConditionType_NotSee = 6,
+// eBoidConditionType_StateTime = 7,
+// eBoidConditionType_Touching = 8,
+// NUM_BOID_CONDITION_TYPES
+//} BoidConditionType;
+//typedef struct BoidCondition {
+// struct BoidCondition *next, *prev;
+// int state_id;
+// short type, mode;
+// float threshold, probability;
+//
+// /* signals */
+// int signal_id, channels;
+//} BoidCondition;
+
+typedef enum BoidRulesetType {
+ eBoidRulesetType_Fuzzy = 0,
+ eBoidRulesetType_Random = 1,
+ eBoidRulesetType_Average = 2,
+ NUM_BOID_RULESET_TYPES
+} BoidRulesetType;
+#define BOIDSTATE_CURRENT 1
+typedef struct BoidState {
+ struct BoidState *next, *prev;
+ ListBase rules;
+ ListBase conditions;
+ ListBase actions;
+ char name[32];
+ int id, flag;
+
+ /* rules */
+ int ruleset_type;
+ float rule_fuzziness;
+
+ /* signal */
+ int signal_id, channels;
+ float volume, falloff;
+} BoidState;
+
+// planned for near future
+//typedef struct BoidSignal {
+// struct BoidSignal *next, *prev;
+// float loc[3];
+// float volume, falloff;
+// int id;
+//} BoidSignal;
+//typedef struct BoidSignalDefine {
+// struct BoidSignalDefine *next, *prev;
+// int id, rt;
+// char name[32];
+//} BoidSignalDefine;
+
+//typedef struct BoidSimulationData {
+// ListBase signal_defines;/* list of defined signals */
+// ListBase signals[20]; /* gathers signals from all channels */
+// struct KDTree *signaltrees[20];
+// char channel_names[20][32];
+// int last_signal_id; /* used for incrementing signal ids */
+// int flag; /* switches for drawing stuff */
+//} BoidSimulationData;
+
+typedef struct BoidSettings {
+ int options, last_state_id;
+
+ float landing_smoothness, height;
+ float banking, pitch;
+
+ float health, aggression;
+ float strength, accuracy, range;
+
+ /* flying related */
+ float air_min_speed, air_max_speed;
+ float air_max_acc, air_max_ave;
+ float air_personal_space;
+
+ /* walk/run related */
+ float land_jump_speed, land_max_speed;
+ float land_max_acc, land_max_ave;
+ float land_personal_space;
+ float land_stick_force;
+
+ struct ListBase states;
+} BoidSettings;
+
+/* boidsettings->options */
+#define BOID_ALLOW_FLIGHT 1
+#define BOID_ALLOW_LAND 2
+#define BOID_ALLOW_CLIMB 4
+
+/* boidrule->options */
+//#define BOID_RULE_FOLLOW_LINE 1 /* follow leader */
+//#define BOID_RULE_PREDICT 2 /* goal/avoid */
+//#define BOID_RULE_ARRIVAL 4 /* goal */
+//#define BOID_RULE_LAND 8 /* goal */
+//#define BOID_RULE_WITH_BOIDS 16 /* avoid collision */
+//#define BOID_RULE_WITH_DEFLECTORS 32 /* avoid collision */
+
+#endif
diff --git a/source/blender/makesdna/DNA_dynamicpaint_types.h b/source/blender/makesdna/DNA_dynamicpaint_types.h
index 071b576eda5..17553e98817 100644
--- a/source/blender/makesdna/DNA_dynamicpaint_types.h
+++ b/source/blender/makesdna/DNA_dynamicpaint_types.h
@@ -108,6 +108,8 @@ typedef struct DynamicPaintSurface {
struct EffectorWeights *effector_weights;
/* cache */
+ struct PointCache *pointcache;
+ struct ListBase ptcaches;
int current_frame;
/* surface */
@@ -228,6 +230,7 @@ enum {
typedef struct DynamicPaintBrushSettings {
struct DynamicPaintModifierData *pmd; /* for fast RNA access */
struct DerivedMesh *dm;
+ struct ParticleSystem *psys;
struct Material *mat;
int flags;
diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h
index 9e246075e7d..374104d8b13 100644
--- a/source/blender/makesdna/DNA_ipo_types.h
+++ b/source/blender/makesdna/DNA_ipo_types.h
@@ -407,6 +407,45 @@ typedef struct Ipo {
#define FLUIDSIM_VEL_FORCE_STR 12
#define FLUIDSIM_VEL_FORCE_RADIUS 13
+/* ******************** */
+/* particle ipos */
+
+/* ******* Particle (ID_PA) ******** */
+#define PART_TOTIPO 25
+#define PART_TOTNAM 25
+
+#define PART_EMIT_FREQ 1
+/* #define PART_EMIT_LIFE 2 */ /*UNUSED*/
+#define PART_EMIT_VEL 3
+#define PART_EMIT_AVE 4
+/* #define PART_EMIT_SIZE 5 */ /*UNUSED*/
+
+#define PART_AVE 6
+#define PART_SIZE 7
+#define PART_DRAG 8
+#define PART_BROWN 9
+#define PART_DAMP 10
+#define PART_LENGTH 11
+#define PART_CLUMP 12
+
+#define PART_GRAV_X 13
+#define PART_GRAV_Y 14
+#define PART_GRAV_Z 15
+
+#define PART_KINK_AMP 16
+#define PART_KINK_FREQ 17
+#define PART_KINK_SHAPE 18
+
+#define PART_BB_TILT 19
+
+#define PART_PD_FSTR 20
+#define PART_PD_FFALL 21
+#define PART_PD_FMAXD 22
+
+#define PART_PD2_FSTR 23
+#define PART_PD2_FFALL 24
+#define PART_PD2_FMAXD 25
+
/* -------------------- Defines: Flags and Types ------------------ */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 4a3d330a698..f95533a88f9 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -52,8 +52,8 @@ typedef enum ModifierType {
eModifierType_Smooth = 16,
eModifierType_Cast = 17,
eModifierType_MeshDeform = 18,
- /*eModifierType_ParticleSystem = 19,*/ /* DEPRECATED */
- /*eModifierType_ParticleInstance = 20,*/ /* DEPRECATED */
+ eModifierType_ParticleSystem = 19,
+ eModifierType_ParticleInstance = 20,
eModifierType_Explode = 21,
eModifierType_Cloth = 22,
eModifierType_Collision = 23,
@@ -599,6 +599,8 @@ typedef struct ClothModifierData {
struct Cloth *clothObject; /* The internal data structure for cloth. */
struct ClothSimSettings *sim_parms; /* definition is in DNA_cloth_types.h */
struct ClothCollSettings *coll_parms; /* definition is in DNA_cloth_types.h */
+ struct PointCache *point_cache; /* definition is in DNA_object_force.h */
+ struct ListBase ptcaches;
/* XXX nasty hack, remove once hair can be separated from cloth modifier data */
struct ClothHairData *hairdata;
/* grid geometry values of hair continuum */
@@ -718,6 +720,41 @@ enum {
MOD_MDEF_SURFACE = 1,
};
+typedef struct ParticleSystemModifierData {
+ ModifierData modifier;
+
+ struct ParticleSystem *psys;
+ struct DerivedMesh *dm_final; /* Final DM - its topology may differ from orig mesh. */
+ struct DerivedMesh *dm_deformed; /* Deformed-onle DM - its topology is same as orig mesh one. */
+ int totdmvert, totdmedge, totdmface;
+ short flag, pad;
+} ParticleSystemModifierData;
+
+typedef enum {
+ eParticleSystemFlag_Pars = (1 << 0),
+ eParticleSystemFlag_psys_updated = (1 << 1),
+ eParticleSystemFlag_file_loaded = (1 << 2),
+} ParticleSystemModifierFlag;
+
+typedef enum {
+ eParticleInstanceFlag_Parents = (1 << 0),
+ eParticleInstanceFlag_Children = (1 << 1),
+ eParticleInstanceFlag_Path = (1 << 2),
+ eParticleInstanceFlag_Unborn = (1 << 3),
+ eParticleInstanceFlag_Alive = (1 << 4),
+ eParticleInstanceFlag_Dead = (1 << 5),
+ eParticleInstanceFlag_KeepShape = (1 << 6),
+ eParticleInstanceFlag_UseSize = (1 << 7),
+} ParticleInstanceModifierFlag;
+
+typedef struct ParticleInstanceModifierData {
+ ModifierData modifier;
+
+ struct Object *ob;
+ short psys, flag, axis, pad;
+ float position, random_position;
+} ParticleInstanceModifierData;
+
typedef enum {
eExplodeFlag_CalcFaces = (1 << 0),
eExplodeFlag_PaSize = (1 << 1),
@@ -752,6 +789,7 @@ typedef struct FluidsimModifierData {
ModifierData modifier;
struct FluidsimSettings *fss; /* definition is in DNA_object_fluidsim.h */
+ struct PointCache *point_cache; /* definition is in DNA_object_force.h */
} FluidsimModifierData;
typedef struct ShrinkwrapModifierData {
diff --git a/source/blender/makesdna/DNA_object_fluidsim.h b/source/blender/makesdna/DNA_object_fluidsim.h
index 958aea86339..a714195dd5d 100644
--- a/source/blender/makesdna/DNA_object_fluidsim.h
+++ b/source/blender/makesdna/DNA_object_fluidsim.h
@@ -151,7 +151,7 @@ typedef struct FluidsimSettings {
#define OB_FLUIDSIM_OBSTACLE 8
#define OB_FLUIDSIM_INFLOW 16
#define OB_FLUIDSIM_OUTFLOW 32
-#define OB_FLUIDSIM_PARTICLE 64 /* DEPRECATED */
+#define OB_FLUIDSIM_PARTICLE 64
#define OB_FLUIDSIM_CONTROL 128
#define OB_TYPEFLAG_START 7
diff --git a/source/blender/makesdna/DNA_object_force.h b/source/blender/makesdna/DNA_object_force.h
index 71988d10ecf..59acefeffe4 100644
--- a/source/blender/makesdna/DNA_object_force.h
+++ b/source/blender/makesdna/DNA_object_force.h
@@ -127,6 +127,87 @@ typedef struct EffectorWeights {
/* EffectorWeights->flag */
#define EFF_WEIGHT_DO_HAIR 1
+/* Point cache file data types:
+ * - used as (1<<flag) so poke jahka if you reach the limit of 15
+ * - to add new data types update:
+ * * BKE_ptcache_data_size()
+ * * ptcache_file_init_pointers()
+ */
+#define BPHYS_DATA_INDEX 0
+#define BPHYS_DATA_LOCATION 1
+#define BPHYS_DATA_SMOKE_LOW 1
+#define BPHYS_DATA_VELOCITY 2
+#define BPHYS_DATA_SMOKE_HIGH 2
+#define BPHYS_DATA_ROTATION 3
+#define BPHYS_DATA_DYNAMICPAINT 3
+#define BPHYS_DATA_AVELOCITY 4 /* used for particles */
+#define BPHYS_DATA_XCONST 4 /* used for cloth */
+#define BPHYS_DATA_SIZE 5
+#define BPHYS_DATA_TIMES 6
+#define BPHYS_DATA_BOIDS 7
+
+#define BPHYS_TOT_DATA 8
+
+#define BPHYS_EXTRA_FLUID_SPRINGS 1
+
+typedef struct PTCacheExtra {
+ struct PTCacheExtra *next, *prev;
+ unsigned int type, totdata;
+ void *data;
+} PTCacheExtra;
+
+typedef struct PTCacheMem {
+ struct PTCacheMem *next, *prev;
+ unsigned int frame, totpoint;
+ unsigned int data_types, flag;
+
+ void *data[8]; /* BPHYS_TOT_DATA */
+ void *cur[8]; /* BPHYS_TOT_DATA */
+
+ struct ListBase extradata;
+} PTCacheMem;
+
+typedef struct PointCache {
+ struct PointCache *next, *prev;
+ int flag; /* generic flag */
+
+ int step; /* The number of frames between cached frames.
+ * This should probably be an upper bound for a per point adaptive step in the future,
+ * buf for now it's the same for all points. Without adaptivity this can effect the perceived
+ * simulation quite a bit though. If for example particles are colliding with a horizontal
+ * plane (with high damping) they quickly come to a stop on the plane, however there are still
+ * forces acting on the particle (gravity and collisions), so the particle velocity isn't necessarily
+ * zero for the whole duration of the frame even if the particle seems stationary. If all simulation
+ * frames aren't cached (step > 1) these velocities are interpolated into movement for the non-cached
+ * frames. The result will look like the point is oscillating around the collision location. So for
+ * now cache step should be set to 1 for accurate reproduction of collisions.
+ */
+
+ int simframe; /* current frame of simulation (only if SIMULATION_VALID) */
+ int startframe; /* simulation start frame */
+ int endframe; /* simulation end frame */
+ int editframe; /* frame being edited (runtime only) */
+ int last_exact; /* last exact frame that's cached */
+ int last_valid; /* used for editing cache - what is the last baked frame */
+ int pad;
+
+ /* for external cache files */
+ int totpoint; /* number of cached points */
+ int index; /* modifier stack index */
+ short compression, rt;
+
+ char name[64];
+ char prev_name[64];
+ char info[64];
+ char path[1024]; /* file path, 1024 = FILE_MAX */
+ char *cached_frames; /* array of length endframe-startframe+1 with flags to indicate cached frames */
+ /* can be later used for other per frame flags too if needed */
+ struct ListBase mem_cache;
+
+ struct PTCacheEdit *edit;
+ void (*free_edit)(struct PTCacheEdit *edit); /* free callback */
+} PointCache;
+
typedef struct SBVertex {
float vec[4];
} SBVertex;
@@ -255,6 +336,9 @@ typedef struct SoftBody {
float shearstiff;
float inpush;
+ struct PointCache *pointcache;
+ struct ListBase ptcaches;
+
struct Group *collision_group;
struct EffectorWeights *effector_weights;
@@ -310,6 +394,31 @@ typedef struct SoftBody {
#define PFIELD_Z_POS 1
#define PFIELD_Z_NEG 2
+/* pointcache->flag */
+#define PTCACHE_BAKED 1
+#define PTCACHE_OUTDATED 2
+#define PTCACHE_SIMULATION_VALID 4
+#define PTCACHE_BAKING 8
+//#define PTCACHE_BAKE_EDIT 16
+//#define PTCACHE_BAKE_EDIT_ACTIVE 32
+#define PTCACHE_DISK_CACHE 64
+//#define PTCACHE_QUICK_CACHE 128 /* removed since 2.64 - [#30974], could be added back in a more useful way */
+#define PTCACHE_FRAMES_SKIPPED 256
+#define PTCACHE_EXTERNAL 512
+#define PTCACHE_READ_INFO 1024
+/* don't use the filename of the blendfile the data is linked from (write a local cache) */
+#define PTCACHE_IGNORE_LIBPATH 2048
+/* high resolution cache is saved for smoke for backwards compatibility, so set this flag to know it's a "fake" cache */
+#define PTCACHE_FAKE_SMOKE (1<<12)
+#define PTCACHE_IGNORE_CLEAR (1<<13)
+
+/* PTCACHE_OUTDATED + PTCACHE_FRAMES_SKIPPED */
+#define PTCACHE_REDO_NEEDED 258
+
+#define PTCACHE_COMPRESS_NO 0
+#define PTCACHE_COMPRESS_LZO 1
+#define PTCACHE_COMPRESS_LZMA 2
+
/* ob->softflag */
#define OB_SB_ENABLE 1 /* deprecated, use modifier */
#define OB_SB_GOAL 2
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index ccde6549d9c..d24c7faa9f5 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -51,6 +51,7 @@ struct Material;
struct PartDeflect;
struct SoftBody;
struct FluidsimSettings;
+struct ParticleSystem;
struct DerivedMesh;
struct SculptSession;
struct bGPdata;
@@ -262,6 +263,7 @@ typedef struct Object {
ListBase constraints; /* object constraints */
ListBase nlastrips DNA_DEPRECATED; // XXX deprecated... old animation system
ListBase hooks DNA_DEPRECATED; // XXX deprecated... old animation system
+ ListBase particlesystem; /* particle systems */
struct PartDeflect *pd; /* particle deflector/attractor/collision data */
struct SoftBody *soft; /* if exists, saved in file */
@@ -330,6 +332,9 @@ typedef struct DupliObject {
/* persistent identifier for a dupli object, for inter-frame matching of
* objects with motion blur, or inter-update matching for syncing */
int persistent_id[16]; /* 2*MAX_DUPLI_RECUR */
+
+ /* particle this dupli was generated from */
+ struct ParticleSystem *particle_system;
} DupliObject;
/* **************** OBJECT ********************* */
@@ -668,7 +673,7 @@ typedef enum ObjectMode {
OB_MODE_VERTEX_PAINT = 1 << 2,
OB_MODE_WEIGHT_PAINT = 1 << 3,
OB_MODE_TEXTURE_PAINT = 1 << 4,
- /*OB_MODE_PARTICLE_EDIT = 1 << 5,*/ /* DEPRECATED */
+ OB_MODE_PARTICLE_EDIT = 1 << 5,
OB_MODE_POSE = 1 << 6,
OB_MODE_GPENCIL = 1 << 7, /* NOTE: Just a dummy to make the UI nicer */
} ObjectMode;
diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h
index c26c236b978..8310856510c 100644
--- a/source/blender/makesdna/DNA_outliner_types.h
+++ b/source/blender/makesdna/DNA_outliner_types.h
@@ -90,7 +90,7 @@ enum {
#define TSE_SEQUENCE 26 /* NO ID */
#define TSE_SEQ_STRIP 27 /* NO ID */
#define TSE_SEQUENCE_DUP 28 /* NO ID */
-/* #define TSE_LINKED_PSYS 29 */ /* DEPRECATED */
+#define TSE_LINKED_PSYS 29
#define TSE_RNA_STRUCT 30 /* NO ID */
#define TSE_RNA_PROPERTY 31 /* NO ID */
#define TSE_RNA_ARRAY_ELEM 32 /* NO ID */
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
new file mode 100644
index 00000000000..1deb9bf3787
--- /dev/null
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -0,0 +1,613 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_particle_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_PARTICLE_TYPES_H__
+#define __DNA_PARTICLE_TYPES_H__
+
+#include "DNA_defs.h"
+#include "DNA_ID.h"
+#include "DNA_boid_types.h"
+
+struct AnimData;
+
+typedef struct HairKey {
+ float co[3]; /* location of hair vertex */
+ float time; /* time along hair, default 0-100 */
+ float weight; /* softbody weight */
+ short editflag; /* saved particled edit mode flags */
+ short pad;
+ float world_co[3];
+} HairKey;
+
+typedef struct ParticleKey { /* when changed update size of struct to copy_particleKey()!! */
+ float co[3]; /* location */
+ float vel[3]; /* velocity */
+ float rot[4]; /* rotation quaternion */
+ float ave[3]; /* angular velocity */
+ float time; /* when this key happens */
+} ParticleKey;
+
+typedef struct BoidParticle {
+ struct Object *ground;
+ struct BoidData data;
+ float gravity[3];
+ float wander[3];
+ float rt;
+} BoidParticle;
+
+typedef struct ParticleSpring {
+ float rest_length;
+ unsigned int particle_index[2], delete_flag;
+} ParticleSpring;
+
+/* Child particles are created around or between parent particles */
+typedef struct ChildParticle {
+ int num, parent; /* num is face index on the final derived mesh */
+ int pa[4]; /* nearest particles to the child, used for the interpolation */
+ float w[4]; /* interpolation weights for the above particles */
+ float fuv[4], foffset; /* face vertex weights and offset */
+ float rt;
+} ChildParticle;
+
+typedef struct ParticleTarget {
+ struct ParticleTarget *next, *prev;
+ struct Object *ob;
+ int psys;
+ short flag, mode;
+ float time, duration;
+} ParticleTarget;
+
+typedef struct ParticleDupliWeight {
+ struct ParticleDupliWeight *next, *prev;
+ struct Object *ob;
+ short count;
+ short flag;
+ short index, rt; /* only updated on file save and used on file load */
+} ParticleDupliWeight;
+
+typedef struct ParticleData {
+ ParticleKey state; /* current global coordinates */
+
+ ParticleKey prev_state; /* previous state */
+
+ HairKey *hair; /* hair vertices */
+
+ ParticleKey *keys; /* keyed keys */
+
+ BoidParticle *boid; /* boids data */
+
+ int totkey; /* amount of hair or keyed keys*/
+
+ float time, lifetime; /* dietime is not nescessarily time+lifetime as */
+ float dietime; /* particles can die unnaturally (collision) */
+
+ /* WARNING! Those two indices, when not affected to vertices, are for !!! TESSELLATED FACES !!!, not POLYGONS! */
+ int num; /* index to vert/edge/face */
+ int num_dmcache; /* index to derived mesh data (face) to avoid slow lookups */
+
+ float fuv[4], foffset; /* coordinates on face/edge number "num" and depth along*/
+ /* face normal for volume emission */
+
+ float size; /* size and multiplier so that we can update size when ever */
+
+ float sphdensity; /* density of sph particle */
+ int pad;
+
+ int hair_index;
+ short flag;
+ short alive; /* the life state of a particle */
+} ParticleData;
+
+typedef struct SPHFluidSettings {
+ /*Particle Fluid*/
+ float radius, spring_k, rest_length;
+ float plasticity_constant, yield_ratio;
+ float plasticity_balance, yield_balance;
+ float viscosity_omega, viscosity_beta;
+ float stiffness_k, stiffness_knear, rest_density;
+ float buoyancy;
+ int flag, spring_frames;
+ short solver;
+ short pad[3];
+} SPHFluidSettings;
+
+/* fluid->flag */
+#define SPH_VISCOELASTIC_SPRINGS 1
+#define SPH_CURRENT_REST_LENGTH 2
+#define SPH_FAC_REPULSION 4
+#define SPH_FAC_DENSITY 8
+#define SPH_FAC_RADIUS 16
+#define SPH_FAC_VISCOSITY 32
+#define SPH_FAC_REST_LENGTH 64
+
+/* fluid->solver (numerical ID field, not bitfield) */
+#define SPH_SOLVER_DDR 0
+#define SPH_SOLVER_CLASSICAL 1
+
+typedef struct ParticleSettings {
+ ID id;
+ struct AnimData *adt;
+
+ struct BoidSettings *boids;
+ struct SPHFluidSettings *fluid;
+
+ struct EffectorWeights *effector_weights;
+ struct Group *collision_group;
+
+ int flag, rt;
+ short type, from, distr, texact;
+ /* physics modes */
+ short phystype, rotmode, avemode, reactevent;
+ int draw, pad1;
+ short draw_as, draw_size, childtype, pad2;
+ short ren_as, subframes, draw_col;
+ /* number of path segments, power of 2 except */
+ short draw_step, ren_step;
+ short hair_step, keys_step;
+
+ /* adaptive path rendering */
+ short adapt_angle, adapt_pix;
+
+ short disp, omat, interpolation, integrator;
+ short rotfrom DNA_DEPRECATED;
+ short kink, kink_axis;
+
+ /* billboards */
+ short bb_align, bb_uv_split, bb_anim, bb_split_offset;
+ float bb_tilt, bb_rand_tilt, bb_offset[2], bb_size[2], bb_vel_head, bb_vel_tail;
+
+ /* draw color */
+ float color_vec_max;
+
+ /* simplification */
+ short simplify_flag, simplify_refsize;
+ float simplify_rate, simplify_transition;
+ float simplify_viewport;
+
+ /* time and emission */
+ float sta, end, lifetime, randlife;
+ float timetweak, courant_target;
+ float jitfac, eff_hair, grid_rand, ps_offset[1];
+ int totpart, userjit, grid_res, effector_amount;
+ short time_flag, time_pad[3];
+
+ /* initial velocity factors */
+ float normfac, obfac, randfac, partfac, tanfac, tanphase, reactfac;
+ float ob_vel[3];
+ float avefac, phasefac, randrotfac, randphasefac;
+ /* physical properties */
+ float mass, size, randsize;
+ /* global physical properties */
+ float acc[3], dragfac, brownfac, dampfac;
+ /* length */
+ float randlength;
+ /* children */
+ int child_flag;
+ int pad3;
+ int child_nbr, ren_child_nbr;
+ float parents, childsize, childrandsize;
+ float childrad, childflat;
+ /* clumping */
+ float clumpfac, clumppow;
+ /* kink */
+ float kink_amp, kink_freq, kink_shape, kink_flat;
+ float kink_amp_clump;
+ int kink_extra_steps, pad4;
+ float kink_axis_random, kink_amp_random;
+ /* rough */
+ float rough1, rough1_size;
+ float rough2, rough2_size, rough2_thres;
+ float rough_end, rough_end_shape;
+ /* length */
+ float clength, clength_thres;
+ /* parting */
+ float parting_fac;
+ float parting_min, parting_max;
+ /* branching */
+ float branch_thres;
+ /* drawing stuff */
+ float draw_line[2];
+ float path_start, path_end;
+ int trail_count;
+ /* keyed particles */
+ int keyed_loops;
+ struct CurveMapping *clumpcurve;
+ struct CurveMapping *roughcurve;
+ float clump_noise_size;
+
+ /* hair dynamics */
+ float bending_random;
+
+ struct MTex *mtex[18]; /* MAX_MTEX */
+
+ struct Group *dup_group;
+ struct ListBase dupliweights;
+ struct Group *eff_group DNA_DEPRECATED; // deprecated
+ struct Object *dup_ob;
+ struct Object *bb_ob;
+ struct Ipo *ipo DNA_DEPRECATED; /* old animation system, deprecated for 2.5 */
+ struct PartDeflect *pd;
+ struct PartDeflect *pd2;
+
+ /* modified dm support */
+ short use_modifier_stack;
+ short pad5[3];
+
+} ParticleSettings;
+
+typedef struct ParticleSystem {
+ /* note1: make sure all (runtime) are NULL's in 'copy_particlesystem' XXX, this function is no more! - need to invstigate */
+ /* note2: make sure any uses of this struct in DNA are accounted for in 'BKE_object_copy_particlesystems' */
+
+ struct ParticleSystem *next, *prev;
+
+ ParticleSettings *part; /* particle settings */
+
+ ParticleData *particles; /* (parent) particles */
+ ChildParticle *child; /* child particles */
+
+ struct PTCacheEdit *edit; /* particle editmode (runtime) */
+ void (*free_edit)(struct PTCacheEdit *edit); /* free callback */
+
+ struct ParticleCacheKey **pathcache; /* path cache (runtime) */
+ struct ParticleCacheKey **childcache; /* child cache (runtime) */
+ ListBase pathcachebufs, childcachebufs; /* buffers for the above */
+
+ struct ClothModifierData *clmd; /* cloth simulation for hair */
+ struct DerivedMesh *hair_in_dm, *hair_out_dm; /* input/output for cloth simulation */
+
+ struct Object *target_ob;
+
+ struct LatticeDeformData *lattice_deform_data; /* run-time only lattice deformation data */
+
+ struct Object *parent; /* particles from global space -> parent space */
+
+ struct ListBase targets; /* used for keyed and boid physics */
+
+ char name[64]; /* particle system name, MAX_NAME */
+
+ float imat[4][4]; /* used for duplicators */
+ float cfra, tree_frame, bvhtree_frame;
+ int seed, child_seed;
+ int flag, totpart, totunexist, totchild, totcached, totchildcache;
+ short recalc, target_psys, totkeyed, bakespace;
+
+ char bb_uvname[3][64]; /* billboard uv name, MAX_CUSTOMDATA_LAYER_NAME */
+
+ /* if you change these remember to update array lengths to PSYS_TOT_VG! */
+ short vgroup[12], vg_neg, rt3; /* vertex groups, 0==disable, 1==starting index */
+
+ /* temporary storage during render */
+ struct ParticleRenderData *renderdata;
+
+ /* point cache */
+ struct PointCache *pointcache;
+ struct ListBase ptcaches;
+
+ struct ListBase *effectors;
+
+ ParticleSpring *fluid_springs;
+ int tot_fluidsprings, alloc_fluidsprings;
+
+ struct KDTree *tree; /* used for interactions with self and other systems */
+ struct BVHTree *bvhtree; /* used for interactions with self and other systems */
+
+ struct ParticleDrawData *pdd;
+
+ float dt_frac; /* current time step, as a fraction of a frame */
+ float _pad; /* spare capacity */
+} ParticleSystem;
+
+typedef enum eParticleDrawFlag {
+ PART_DRAW_VEL = (1 << 0),
+ PART_DRAW_GLOBAL_OB = (1 << 1),
+ PART_DRAW_SIZE = (1 << 2),
+ PART_DRAW_EMITTER = (1 << 3), /* render emitter also */
+ PART_DRAW_HEALTH = (1 << 4),
+ PART_ABS_PATH_TIME = (1 << 5),
+ PART_DRAW_COUNT_GR = (1 << 6),
+ PART_DRAW_BB_LOCK = (1 << 7), /* used with billboards */
+ PART_DRAW_ROTATE_OB = (1 << 7), /* used with dupliobjects/groups */
+ PART_DRAW_PARENT = (1 << 8),
+ PART_DRAW_NUM = (1 << 9),
+ PART_DRAW_RAND_GR = (1 << 10),
+ PART_DRAW_REN_ADAPT = (1 << 11),
+ PART_DRAW_VEL_LENGTH = (1 << 12),
+ PART_DRAW_MAT_COL = (1 << 13), /* deprecated, but used in do_versions */
+ PART_DRAW_WHOLE_GR = (1 << 14),
+ PART_DRAW_REN_STRAND = (1 << 15),
+ PART_DRAW_NO_SCALE_OB = (1 << 16), /* used with dupliobjects/groups */
+ PART_DRAW_GUIDE_HAIRS = (1 << 17),
+ PART_DRAW_HAIR_GRID = (1 << 18),
+} eParticleDrawFlag;
+
+/* part->type */
+/* hair is allways baked static in object/geometry space */
+/* other types (normal particles) are in global space and not static baked */
+#define PART_EMITTER 0
+//#define PART_REACTOR 1
+#define PART_HAIR 2
+#define PART_FLUID 3
+
+/* part->flag */
+#define PART_REACT_STA_END 1
+#define PART_REACT_MULTIPLE 2
+
+//#define PART_LOOP 4 /* not used anymore */
+ /* for dopesheet */
+#define PART_DS_EXPAND 8
+
+#define PART_HAIR_REGROW 16 /* regrow hair for each frame */
+
+#define PART_UNBORN 32 /*show unborn particles*/
+#define PART_DIED 64 /*show died particles*/
+
+#define PART_TRAND 128
+#define PART_EDISTR 256 /* particle/face from face areas */
+
+#define PART_ROTATIONS 512 /* calculate particle rotations (and store them in pointcache) */
+#define PART_DIE_ON_COL (1<<12)
+#define PART_SIZE_DEFL (1<<13) /* swept sphere deflections */
+#define PART_ROT_DYN (1<<14) /* dynamic rotation */
+#define PART_SIZEMASS (1<<16)
+
+#define PART_HIDE_ADVANCED_HAIR (1<<15)
+
+//#define PART_ABS_TIME (1<<17)
+//#define PART_GLOB_TIME (1<<18)
+
+#define PART_BOIDS_2D (1<<19)
+
+//#define PART_BRANCHING (1<<20)
+//#define PART_ANIM_BRANCHING (1<<21)
+
+#define PART_HAIR_BSPLINE 1024
+
+#define PART_GRID_HEXAGONAL (1<<24)
+#define PART_GRID_INVERT (1<<26)
+
+#define PART_CHILD_EFFECT (1<<27)
+#define PART_CHILD_LONG_HAIR (1<<28)
+/* #define PART_CHILD_RENDER (1<<29) */ /*UNUSED*/
+#define PART_CHILD_GUIDE (1<<30)
+
+#define PART_SELF_EFFECT (1<<22)
+
+/* part->from */
+#define PART_FROM_VERT 0
+#define PART_FROM_FACE 1
+#define PART_FROM_VOLUME 2
+/* #define PART_FROM_PARTICLE 3 deprecated! */
+#define PART_FROM_CHILD 4
+
+/* part->distr */
+#define PART_DISTR_JIT 0
+#define PART_DISTR_RAND 1
+#define PART_DISTR_GRID 2
+
+/* part->phystype */
+#define PART_PHYS_NO 0
+#define PART_PHYS_NEWTON 1
+#define PART_PHYS_KEYED 2
+#define PART_PHYS_BOIDS 3
+#define PART_PHYS_FLUID 4
+
+/* part->kink */
+typedef enum eParticleKink {
+ PART_KINK_NO = 0,
+ PART_KINK_CURL = 1,
+ PART_KINK_RADIAL = 2,
+ PART_KINK_WAVE = 3,
+ PART_KINK_BRAID = 4,
+ PART_KINK_SPIRAL = 5,
+} eParticleKink;
+
+/* part->child_flag */
+typedef enum eParticleChildFlag {
+ PART_CHILD_USE_CLUMP_NOISE = (1<<0),
+ PART_CHILD_USE_CLUMP_CURVE = (1<<1),
+ PART_CHILD_USE_ROUGH_CURVE = (1<<2),
+} eParticleChildFlag;
+
+/* part->draw_col */
+#define PART_DRAW_COL_NONE 0
+#define PART_DRAW_COL_MAT 1
+#define PART_DRAW_COL_VEL 2
+#define PART_DRAW_COL_ACC 3
+
+
+/* part->simplify_flag */
+#define PART_SIMPLIFY_ENABLE 1
+#define PART_SIMPLIFY_VIEWPORT 2
+
+/* part->time_flag */
+#define PART_TIME_AUTOSF 1 /* Automatic subframes */
+
+/* part->bb_align */
+#define PART_BB_X 0
+#define PART_BB_Y 1
+#define PART_BB_Z 2
+#define PART_BB_VIEW 3
+#define PART_BB_VEL 4
+
+/* part->bb_anim */
+#define PART_BB_ANIM_NONE 0
+#define PART_BB_ANIM_AGE 1
+#define PART_BB_ANIM_ANGLE 2
+#define PART_BB_ANIM_FRAME 3
+
+/* part->bb_split_offset */
+#define PART_BB_OFF_NONE 0
+#define PART_BB_OFF_LINEAR 1
+#define PART_BB_OFF_RANDOM 2
+
+/* part->draw_as */
+/* part->ren_as*/
+#define PART_DRAW_NOT 0
+#define PART_DRAW_DOT 1
+#define PART_DRAW_HALO 1
+#define PART_DRAW_CIRC 2
+#define PART_DRAW_CROSS 3
+#define PART_DRAW_AXIS 4
+#define PART_DRAW_LINE 5
+#define PART_DRAW_PATH 6
+#define PART_DRAW_OB 7
+#define PART_DRAW_GR 8
+#define PART_DRAW_BB 9
+#define PART_DRAW_REND 10
+
+/* part->integrator */
+#define PART_INT_EULER 0
+#define PART_INT_MIDPOINT 1
+#define PART_INT_RK4 2
+#define PART_INT_VERLET 3
+
+/* part->rotmode */
+#define PART_ROT_NOR 1
+#define PART_ROT_VEL 2
+#define PART_ROT_GLOB_X 3
+#define PART_ROT_GLOB_Y 4
+#define PART_ROT_GLOB_Z 5
+#define PART_ROT_OB_X 6
+#define PART_ROT_OB_Y 7
+#define PART_ROT_OB_Z 8
+#define PART_ROT_NOR_TAN 9
+
+/* part->avemode */
+#define PART_AVE_VELOCITY 1
+#define PART_AVE_RAND 2
+#define PART_AVE_HORIZONTAL 3
+#define PART_AVE_VERTICAL 4
+#define PART_AVE_GLOBAL_X 5
+#define PART_AVE_GLOBAL_Y 6
+#define PART_AVE_GLOBAL_Z 7
+
+/* part->reactevent */
+#define PART_EVENT_DEATH 0
+#define PART_EVENT_COLLIDE 1
+#define PART_EVENT_NEAR 2
+
+/* part->childtype */
+#define PART_CHILD_PARTICLES 1
+#define PART_CHILD_FACES 2
+
+/* psys->recalc */
+/* starts from (1 << 3) so that the first bits can be ob->recalc */
+#define PSYS_RECALC_REDO (1 << 3) /* only do pathcache etc */
+#define PSYS_RECALC_RESET (1 << 4) /* reset everything including pointcache */
+#define PSYS_RECALC_TYPE (1 << 5) /* handle system type change */
+#define PSYS_RECALC_CHILD (1 << 6) /* only child settings changed */
+#define PSYS_RECALC_PHYS (1 << 7) /* physics type changed */
+#define PSYS_RECALC (PSYS_RECALC_REDO | PSYS_RECALC_RESET | PSYS_RECALC_TYPE | PSYS_RECALC_CHILD | PSYS_RECALC_PHYS)
+
+/* psys->flag */
+#define PSYS_CURRENT 1
+#define PSYS_GLOBAL_HAIR 2
+#define PSYS_HAIR_DYNAMICS 4
+#define PSYS_KEYED_TIMING 8
+//#define PSYS_ENABLED 16 /* deprecated */
+#define PSYS_HAIR_UPDATED 32 /* signal for updating hair particle mode */
+#define PSYS_DRAWING 64
+#define PSYS_USE_IMAT 128
+#define PSYS_DELETE 256 /* remove particlesystem as soon as possible */
+#define PSYS_HAIR_DONE 512
+#define PSYS_KEYED 1024
+#define PSYS_EDITED 2048
+//#define PSYS_PROTECT_CACHE 4096 /* deprecated */
+#define PSYS_DISABLED 8192
+#define PSYS_OB_ANIM_RESTORE 16384 /* runtime flag */
+
+/* pars->flag */
+#define PARS_UNEXIST 1
+#define PARS_NO_DISP 2
+//#define PARS_STICKY 4 /* deprecated */
+#define PARS_REKEY 8
+
+/* pars->alive */
+//#define PARS_KILLED 0 /* deprecated */
+#define PARS_DEAD 1
+#define PARS_UNBORN 2
+#define PARS_ALIVE 3
+#define PARS_DYING 4
+
+/* ParticleDupliWeight->flag */
+#define PART_DUPLIW_CURRENT 1
+
+/* psys->vg */
+#define PSYS_TOT_VG 12
+
+#define PSYS_VG_DENSITY 0
+#define PSYS_VG_VEL 1
+#define PSYS_VG_LENGTH 2
+#define PSYS_VG_CLUMP 3
+#define PSYS_VG_KINK 4
+#define PSYS_VG_ROUGH1 5
+#define PSYS_VG_ROUGH2 6
+#define PSYS_VG_ROUGHE 7
+#define PSYS_VG_SIZE 8
+#define PSYS_VG_TAN 9
+#define PSYS_VG_ROT 10
+#define PSYS_VG_EFFECTOR 11
+
+/* ParticleTarget->flag */
+#define PTARGET_CURRENT 1
+#define PTARGET_VALID 2
+
+/* ParticleTarget->mode */
+#define PTARGET_MODE_NEUTRAL 0
+#define PTARGET_MODE_FRIEND 1
+#define PTARGET_MODE_ENEMY 2
+
+/* mapto */
+typedef enum eParticleTextureInfluence {
+ /* init */
+ PAMAP_TIME = (1<<0), /* emission time */
+ PAMAP_LIFE = (1<<1), /* life time */
+ PAMAP_DENS = (1<<2), /* density */
+ PAMAP_SIZE = (1<<3), /* physical size */
+ PAMAP_INIT = (PAMAP_TIME | PAMAP_LIFE | PAMAP_DENS | PAMAP_SIZE),
+ /* reset */
+ PAMAP_IVEL = (1<<5), /* initial velocity */
+ /* physics */
+ PAMAP_FIELD = (1<<6), /* force fields */
+ PAMAP_GRAVITY = (1<<10),
+ PAMAP_DAMP = (1<<11),
+ PAMAP_PHYSICS = (PAMAP_FIELD | PAMAP_GRAVITY | PAMAP_DAMP),
+ /* children */
+ PAMAP_CLUMP = (1<<7),
+ PAMAP_KINK_FREQ = (1<<8),
+ PAMAP_KINK_AMP = (1<<12),
+ PAMAP_ROUGH = (1<<9),
+ PAMAP_LENGTH = (1<<4),
+ PAMAP_CHILD = (PAMAP_CLUMP | PAMAP_KINK_FREQ | PAMAP_KINK_AMP | PAMAP_ROUGH | PAMAP_LENGTH),
+} eParticleTextureInfluence;
+
+#endif
diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h
index 72805c7acb4..381ee5d40e5 100644
--- a/source/blender/makesdna/DNA_rigidbody_types.h
+++ b/source/blender/makesdna/DNA_rigidbody_types.h
@@ -58,6 +58,9 @@ typedef struct RigidBodyWorld {
int pad;
float ltime; /* last frame world was evaluated for (internal) */
+ /* cache */
+ struct PointCache *pointcache;
+ struct ListBase ptcaches;
int numbodies; /* number of objects in rigid body group */
short steps_per_second; /* number of simulation steps thaken per second */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index cf367bf3205..f5e71ae59a9 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1046,6 +1046,39 @@ typedef struct ImagePaintSettings {
} ImagePaintSettings;
/* ------------------------------------------- */
+/* Particle Edit */
+
+/* Settings for a Particle Editing Brush */
+typedef struct ParticleBrushData {
+ short size; /* common setting */
+ short step, invert, count; /* for specific brushes only */
+ int flag;
+ float strength;
+} ParticleBrushData;
+
+/* Particle Edit Mode Settings */
+typedef struct ParticleEditSettings {
+ short flag;
+ short totrekey;
+ short totaddkey;
+ short brushtype;
+
+ ParticleBrushData brush[7]; /* 7 = PE_TOT_BRUSH */
+ void *paintcursor; /* runtime */
+
+ float emitterdist, rt;
+
+ int selectmode;
+ int edittype;
+
+ int draw_step, fade_frames;
+
+ struct Scene *scene;
+ struct Object *object;
+ struct Object *shape_object;
+} ParticleEditSettings;
+
+/* ------------------------------------------- */
/* Sculpt */
/* Sculpt */
@@ -1409,6 +1442,9 @@ typedef struct ToolSettings {
/* Image Paint (8 byttse aligned please!) */
struct ImagePaintSettings imapaint;
+ /* Particle Editing */
+ struct ParticleEditSettings particle;
+
/* Transform Proportional Area of Effect */
float proportional_size;
diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h
index 4ee83346fe3..c95e0a1f54a 100644
--- a/source/blender/makesdna/DNA_smoke_types.h
+++ b/source/blender/makesdna/DNA_smoke_types.h
@@ -188,6 +188,9 @@ typedef struct SmokeDomainSettings {
char data_depth;
char pad[2];
+ /* Smoke uses only one cache from now on (index [0]), but keeping the array for now for reading old files. */
+ struct PointCache *point_cache[2]; /* definition is in DNA_object_force.h */
+ struct ListBase ptcaches[2];
struct EffectorWeights *effector_weights;
int border_collisions; /* How domain border collisions are handled */
float time_scale;
@@ -242,6 +245,7 @@ typedef struct SmokeDomainSettings {
typedef struct SmokeFlowSettings {
struct SmokeModifierData *smd; /* for fast RNA access */
struct DerivedMesh *dm;
+ struct ParticleSystem *psys;
struct Tex *noise_texture;
/* initial velocity */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index d1b1074e479..5e015544dc9 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -174,7 +174,7 @@ typedef enum eSpaceButtons_Context {
BCONTEXT_DATA = 4,
BCONTEXT_MATERIAL = 5,
BCONTEXT_TEXTURE = 6,
- /*BCONTEXT_PARTICLE = 7,*/ /* DEPRECATED */
+ BCONTEXT_PARTICLE = 7,
BCONTEXT_PHYSICS = 8,
BCONTEXT_BONE = 9,
BCONTEXT_MODIFIER = 10,
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 1c4db5289ef..1dc6c7ab578 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -749,7 +749,7 @@ typedef enum eDupli_ID_Flags {
USER_DUP_TEX = (1 << 8),
USER_DUP_ARM = (1 << 9),
USER_DUP_ACT = (1 << 10),
- /*USER_DUP_PSYS = (1 << 11),*/ /* DEPRECATED */
+ USER_DUP_PSYS = (1 << 11)
} eDupli_ID_Flags;
/* gameflags */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 0f7ed8c0bc0..2cea8715a65 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -114,10 +114,12 @@ static const char *includefiles[] = {
"DNA_color_types.h",
"DNA_brush_types.h",
"DNA_customdata_types.h",
+ "DNA_particle_types.h",
"DNA_cloth_types.h",
"DNA_gpencil_types.h",
"DNA_windowmanager_types.h",
"DNA_anim_types.h",
+ "DNA_boid_types.h",
"DNA_smoke_types.h",
"DNA_speaker_types.h",
"DNA_movieclip_types.h",
@@ -1324,10 +1326,12 @@ int main(int argc, char **argv)
#include "DNA_color_types.h"
#include "DNA_brush_types.h"
#include "DNA_customdata_types.h"
+#include "DNA_particle_types.h"
#include "DNA_cloth_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_anim_types.h"
+#include "DNA_boid_types.h"
#include "DNA_smoke_types.h"
#include "DNA_speaker_types.h"
#include "DNA_movieclip_types.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 7f20ac4acb2..f97a5735c94 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -78,6 +78,15 @@ extern StructRNA RNA_BezierSplinePoint;
extern StructRNA RNA_BlendData;
extern StructRNA RNA_BlendTexture;
extern StructRNA RNA_BlenderRNA;
+extern StructRNA RNA_BoidRule;
+extern StructRNA RNA_BoidRuleAverageSpeed;
+extern StructRNA RNA_BoidRuleAvoid;
+extern StructRNA RNA_BoidRuleAvoidCollision;
+extern StructRNA RNA_BoidRuleFight;
+extern StructRNA RNA_BoidRuleFollowLeader;
+extern StructRNA RNA_BoidRuleGoal;
+extern StructRNA RNA_BoidSettings;
+extern StructRNA RNA_BoidState;
extern StructRNA RNA_Bone;
extern StructRNA RNA_BoneGroup;
extern StructRNA RNA_BooleanModifier;
@@ -91,6 +100,7 @@ extern StructRNA RNA_CacheFile;
extern StructRNA RNA_Camera;
extern StructRNA RNA_CastModifier;
extern StructRNA RNA_ChildOfConstraint;
+extern StructRNA RNA_ChildParticle;
extern StructRNA RNA_ClampToConstraint;
extern StructRNA RNA_ClothCollisionSettings;
extern StructRNA RNA_ClothModifier;
@@ -450,7 +460,21 @@ extern StructRNA RNA_PaintCurve;
extern StructRNA RNA_Palette;
extern StructRNA RNA_PaletteColor;
extern StructRNA RNA_Panel;
+extern StructRNA RNA_Particle;
+extern StructRNA RNA_ParticleBrush;
+extern StructRNA RNA_ParticleDupliWeight;
+extern StructRNA RNA_ParticleEdit;
+extern StructRNA RNA_ParticleFluidSettings;
+extern StructRNA RNA_ParticleHairKey;
+extern StructRNA RNA_ParticleInstanceModifier;
+extern StructRNA RNA_ParticleKey;
+extern StructRNA RNA_ParticleSettings;
+extern StructRNA RNA_ParticleSettingsTextureSlot;
+extern StructRNA RNA_ParticleSystem;
+extern StructRNA RNA_ParticleSystemModifier;
+extern StructRNA RNA_ParticleTarget;
extern StructRNA RNA_PivotConstraint;
+extern StructRNA RNA_PointCache;
extern StructRNA RNA_PointDensity;
extern StructRNA RNA_PointDensityTexture;
extern StructRNA RNA_PointLamp;
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index 27da7392cbd..1c9b3593d17 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -59,6 +59,7 @@ extern EnumPropertyItem rna_enum_space_type_items[];
extern EnumPropertyItem rna_enum_region_type_items[];
extern EnumPropertyItem rna_enum_object_modifier_type_items[];
extern EnumPropertyItem rna_enum_constraint_type_items[];
+extern EnumPropertyItem rna_enum_boidrule_type_items[];
extern EnumPropertyItem rna_enum_sequence_modifier_type_items[];
extern EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[];
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index cc3fd2ce324..0f3ea27a7f9 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -36,6 +36,7 @@ set(DEFSRC
rna_animation.c
rna_animviz.c
rna_armature.c
+ rna_boid.c
rna_brush.c
rna_cachefile.c
rna_camera.c
@@ -69,6 +70,7 @@ set(DEFSRC
rna_object_force.c
rna_packedfile.c
rna_palette.c
+ rna_particle.c
rna_pose.c
rna_property.c
rna_render.c
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index d9c5865b219..4552c773097 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -3311,6 +3311,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_animviz.c", NULL, RNA_def_animviz},
{"rna_actuator.c", "rna_actuator_api.c", RNA_def_actuator},
{"rna_armature.c", "rna_armature_api.c", RNA_def_armature},
+ {"rna_boid.c", NULL, RNA_def_boid},
{"rna_brush.c", NULL, RNA_def_brush},
{"rna_cachefile.c", NULL, RNA_def_cachefile},
{"rna_camera.c", "rna_camera_api.c", RNA_def_camera},
@@ -3342,6 +3343,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_object_force.c", NULL, RNA_def_object_force},
{"rna_packedfile.c", NULL, RNA_def_packedfile},
{"rna_palette.c", NULL, RNA_def_palette},
+ {"rna_particle.c", NULL, RNA_def_particle},
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
{"rna_property.c", NULL, RNA_def_gameproperty},
{"rna_render.c", NULL, RNA_def_render},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 0c4eb9ceb66..671902c5cc7 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -73,6 +73,7 @@ EnumPropertyItem rna_enum_id_type_items[] = {
{ID_OB, "OBJECT", ICON_OBJECT_DATA, "Object", ""},
{ID_PC, "PAINTCURVE", ICON_CURVE_BEZCURVE, "Paint Curve", ""},
{ID_PAL, "PALETTE", ICON_COLOR, "Palette", ""},
+ {ID_PA, "PARTICLE", ICON_PARTICLE_DATA, "Particle", ""},
{ID_SCE, "SCENE", ICON_SCENE_DATA, "Scene", ""},
{ID_SCR, "SCREEN", ICON_SPLITSCREEN, "Screen", ""},
{ID_SO, "SOUND", ICON_PLAY_AUDIO, "Sound", ""},
@@ -158,6 +159,7 @@ short RNA_type_to_ID_code(StructRNA *type)
if (RNA_struct_is_a(type, &RNA_Mask)) return ID_MSK;
if (RNA_struct_is_a(type, &RNA_NodeTree)) return ID_NT;
if (RNA_struct_is_a(type, &RNA_Object)) return ID_OB;
+ if (RNA_struct_is_a(type, &RNA_ParticleSettings)) return ID_PA;
if (RNA_struct_is_a(type, &RNA_Palette)) return ID_PAL;
if (RNA_struct_is_a(type, &RNA_PaintCurve)) return ID_PC;
if (RNA_struct_is_a(type, &RNA_Scene)) return ID_SCE;
@@ -197,6 +199,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_MSK: return &RNA_Mask;
case ID_NT: return &RNA_NodeTree;
case ID_OB: return &RNA_Object;
+ case ID_PA: return &RNA_ParticleSettings;
case ID_PAL: return &RNA_Palette;
case ID_PC: return &RNA_PaintCurve;
case ID_SCE: return &RNA_Scene;
@@ -313,6 +316,15 @@ static void rna_ID_update_tag(ID *id, ReportList *reports, int flag)
return;
}
break;
+ /* Could add particle updates later */
+#if 0
+ case ID_PA:
+ if (flag & ~(OB_RECALC_ALL | PSYS_RECALC)) {
+ BKE_report(reports, RPT_ERROR, "'Refresh' incompatible with ParticleSettings ID type");
+ return;
+ }
+ break;
+#endif
default:
BKE_report(reports, RPT_ERROR, "This ID type is not compatible with any 'refresh' options");
return;
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index cd45cad00b9..0c4c7ddac81 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -476,6 +476,12 @@ static void rna_def_dopesheet(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_SCENE_DATA, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+ prop = RNA_def_property(srna, "show_particles", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag", ADS_FILTER_NOPART);
+ RNA_def_property_ui_text(prop, "Display Particle", "Include visualization of particle related animation data");
+ RNA_def_property_ui_icon(prop, ICON_PARTICLE_DATA, 0);
+ RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+
prop = RNA_def_property(srna, "show_metaballs", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag", ADS_FILTER_NOMBA);
RNA_def_property_ui_text(prop, "Display Metaball", "Include visualization of metaball related animation data");
diff --git a/source/blender/makesrna/intern/rna_boid.c b/source/blender/makesrna/intern/rna_boid.c
new file mode 100644
index 00000000000..72f67b86c23
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_boid.c
@@ -0,0 +1,674 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2009 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_boid.c
+ * \ingroup RNA
+ */
+
+#include <float.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "DNA_scene_types.h"
+#include "DNA_boid_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BLI_utildefines.h"
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+EnumPropertyItem rna_enum_boidrule_type_items[] = {
+ {eBoidRuleType_Goal, "GOAL", 0, "Goal", "Go to assigned object or loudest assigned signal source"},
+ {eBoidRuleType_Avoid, "AVOID", 0, "Avoid", "Get away from assigned object or loudest assigned signal source"},
+ {eBoidRuleType_AvoidCollision, "AVOID_COLLISION", 0, "Avoid Collision",
+ "Maneuver to avoid collisions with other boids and deflector objects in "
+ "near future"},
+ {eBoidRuleType_Separate, "SEPARATE", 0, "Separate", "Keep from going through other boids"},
+ {eBoidRuleType_Flock, "FLOCK", 0, "Flock", "Move to center of neighbors and match their velocity"},
+ {eBoidRuleType_FollowLeader, "FOLLOW_LEADER", 0, "Follow Leader", "Follow a boid or assigned object"},
+ {eBoidRuleType_AverageSpeed, "AVERAGE_SPEED", 0, "Average Speed", "Maintain speed, flight level or wander"},
+ {eBoidRuleType_Fight, "FIGHT", 0, "Fight", "Go to closest enemy and attack when in range"},
+#if 0
+ {eBoidRuleType_Protect, "PROTECT", 0, "Protect", "Go to enemy closest to target and attack when in range"},
+ {eBoidRuleType_Hide, "HIDE", 0, "Hide", "Find a deflector move to it's other side from closest enemy"},
+ {eBoidRuleType_FollowPath, "FOLLOW_PATH", 0, "Follow Path",
+ "Move along a assigned curve or closest curve in a group"},
+ {eBoidRuleType_FollowWall, "FOLLOW_WALL", 0, "Follow Wall",
+ "Move next to a deflector object's in direction of it's tangent"},
+#endif
+ {0, NULL, 0, NULL, NULL}
+};
+
+#ifndef RNA_RUNTIME
+static EnumPropertyItem boidruleset_type_items[] = {
+ {eBoidRulesetType_Fuzzy, "FUZZY", 0, "Fuzzy",
+ "Rules are gone through top to bottom (only the first rule which effect is above "
+ "fuzziness threshold is evaluated)"},
+ {eBoidRulesetType_Random, "RANDOM", 0, "Random", "A random rule is selected for each boid"},
+ {eBoidRulesetType_Average, "AVERAGE", 0, "Average", "All rules are averaged"},
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+
+#ifdef RNA_RUNTIME
+
+#include "BLI_math_base.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_particle.h"
+
+static void rna_Boids_reset(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ if (ptr->type == &RNA_ParticleSystem) {
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ psys->recalc = PSYS_RECALC_RESET;
+
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
+ }
+ else
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA | PSYS_RECALC_RESET);
+
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+}
+static void rna_Boids_reset_deps(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ if (ptr->type == &RNA_ParticleSystem) {
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ psys->recalc = PSYS_RECALC_RESET;
+
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
+ }
+ else
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA | PSYS_RECALC_RESET);
+
+ DAG_relations_tag_update(bmain);
+
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+}
+
+static StructRNA *rna_BoidRule_refine(struct PointerRNA *ptr)
+{
+ BoidRule *rule = (BoidRule *)ptr->data;
+
+ switch (rule->type) {
+ case eBoidRuleType_Goal:
+ return &RNA_BoidRuleGoal;
+ case eBoidRuleType_Avoid:
+ return &RNA_BoidRuleAvoid;
+ case eBoidRuleType_AvoidCollision:
+ return &RNA_BoidRuleAvoidCollision;
+ case eBoidRuleType_FollowLeader:
+ return &RNA_BoidRuleFollowLeader;
+ case eBoidRuleType_AverageSpeed:
+ return &RNA_BoidRuleAverageSpeed;
+ case eBoidRuleType_Fight:
+ return &RNA_BoidRuleFight;
+ default:
+ return &RNA_BoidRule;
+ }
+}
+
+static char *rna_BoidRule_path(PointerRNA *ptr)
+{
+ BoidRule *rule = (BoidRule *)ptr->data;
+ char name_esc[sizeof(rule->name) * 2];
+
+ BLI_strescape(name_esc, rule->name, sizeof(name_esc));
+
+ return BLI_sprintfN("rules[\"%s\"]", name_esc); /* XXX not unique */
+}
+
+static PointerRNA rna_BoidState_active_boid_rule_get(PointerRNA *ptr)
+{
+ BoidState *state = (BoidState *)ptr->data;
+ BoidRule *rule = (BoidRule *)state->rules.first;
+
+ for (; rule; rule = rule->next) {
+ if (rule->flag & BOIDRULE_CURRENT)
+ return rna_pointer_inherit_refine(ptr, &RNA_BoidRule, rule);
+ }
+ return rna_pointer_inherit_refine(ptr, &RNA_BoidRule, NULL);
+}
+static void rna_BoidState_active_boid_rule_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ BoidState *state = (BoidState *)ptr->data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&state->rules) - 1);
+}
+
+static int rna_BoidState_active_boid_rule_index_get(PointerRNA *ptr)
+{
+ BoidState *state = (BoidState *)ptr->data;
+ BoidRule *rule = (BoidRule *)state->rules.first;
+ int i = 0;
+
+ for (; rule; rule = rule->next, i++) {
+ if (rule->flag & BOIDRULE_CURRENT)
+ return i;
+ }
+ return 0;
+}
+
+static void rna_BoidState_active_boid_rule_index_set(struct PointerRNA *ptr, int value)
+{
+ BoidState *state = (BoidState *)ptr->data;
+ BoidRule *rule = (BoidRule *)state->rules.first;
+ int i = 0;
+
+ for (; rule; rule = rule->next, i++) {
+ if (i == value)
+ rule->flag |= BOIDRULE_CURRENT;
+ else
+ rule->flag &= ~BOIDRULE_CURRENT;
+ }
+}
+
+static int particle_id_check(PointerRNA *ptr)
+{
+ ID *id = ptr->id.data;
+
+ return (GS(id->name) == ID_PA);
+}
+
+static char *rna_BoidSettings_path(PointerRNA *ptr)
+{
+ BoidSettings *boids = (BoidSettings *)ptr->data;
+
+ if (particle_id_check(ptr)) {
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ if (part->boids == boids)
+ return BLI_sprintfN("boids");
+ }
+ return NULL;
+}
+
+static PointerRNA rna_BoidSettings_active_boid_state_get(PointerRNA *ptr)
+{
+ BoidSettings *boids = (BoidSettings *)ptr->data;
+ BoidState *state = (BoidState *)boids->states.first;
+
+ for (; state; state = state->next) {
+ if (state->flag & BOIDSTATE_CURRENT)
+ return rna_pointer_inherit_refine(ptr, &RNA_BoidState, state);
+ }
+ return rna_pointer_inherit_refine(ptr, &RNA_BoidState, NULL);
+}
+static void rna_BoidSettings_active_boid_state_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ BoidSettings *boids = (BoidSettings *)ptr->data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&boids->states) - 1);
+}
+
+static int rna_BoidSettings_active_boid_state_index_get(PointerRNA *ptr)
+{
+ BoidSettings *boids = (BoidSettings *)ptr->data;
+ BoidState *state = (BoidState *)boids->states.first;
+ int i = 0;
+
+ for (; state; state = state->next, i++) {
+ if (state->flag & BOIDSTATE_CURRENT)
+ return i;
+ }
+ return 0;
+}
+
+static void rna_BoidSettings_active_boid_state_index_set(struct PointerRNA *ptr, int value)
+{
+ BoidSettings *boids = (BoidSettings *)ptr->data;
+ BoidState *state = (BoidState *)boids->states.first;
+ int i = 0;
+
+ for (; state; state = state->next, i++) {
+ if (i == value)
+ state->flag |= BOIDSTATE_CURRENT;
+ else
+ state->flag &= ~BOIDSTATE_CURRENT;
+ }
+}
+
+#else
+
+static void rna_def_boidrule_goal(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidRuleGoal", "BoidRule");
+ RNA_def_struct_ui_text(srna, "Goal", "");
+ RNA_def_struct_sdna(srna, "BoidRuleGoalAvoid");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "ob");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Goal object");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset_deps");
+
+ prop = RNA_def_property(srna, "use_predict", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_GOAL_AVOID_PREDICT);
+ RNA_def_property_ui_text(prop, "Predict", "Predict target movement");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_avoid(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidRuleAvoid", "BoidRule");
+ RNA_def_struct_ui_text(srna, "Avoid", "");
+ RNA_def_struct_sdna(srna, "BoidRuleGoalAvoid");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "ob");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object to avoid");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset_deps");
+
+ prop = RNA_def_property(srna, "use_predict", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_GOAL_AVOID_PREDICT);
+ RNA_def_property_ui_text(prop, "Predict", "Predict target movement");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "fear_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Fear factor", "Avoid object if danger from it is above this threshold");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_avoid_collision(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidRuleAvoidCollision", "BoidRule");
+ RNA_def_struct_ui_text(srna, "Avoid Collision", "");
+
+ prop = RNA_def_property(srna, "use_avoid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_ACOLL_WITH_BOIDS);
+ RNA_def_property_ui_text(prop, "Boids", "Avoid collision with other boids");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "use_avoid_collision", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_ACOLL_WITH_DEFLECTORS);
+ RNA_def_property_ui_text(prop, "Deflectors", "Avoid collision with deflector objects");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "look_ahead", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Look ahead", "Time to look ahead in seconds");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_follow_leader(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidRuleFollowLeader", "BoidRule");
+ RNA_def_struct_ui_text(srna, "Follow Leader", "");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "ob");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Follow this object instead of a boid");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset_deps");
+
+ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Distance", "Distance behind leader to follow");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "queue_count", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "queue_size");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Queue Size", "How many boids in a line");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "use_line", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BRULE_LEADER_IN_LINE);
+ RNA_def_property_ui_text(prop, "Line", "Follow leader in a line");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_average_speed(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidRuleAverageSpeed", "BoidRule");
+ RNA_def_struct_ui_text(srna, "Average Speed", "");
+
+ prop = RNA_def_property(srna, "wander", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Wander", "How fast velocity's direction is randomized");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "level", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Level", "How much velocity's z-component is kept constant");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "speed", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Speed", "Percentage of maximum speed");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule_fight(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidRuleFight", "BoidRule");
+ RNA_def_struct_ui_text(srna, "Fight", "");
+
+ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Fight Distance", "Attack boids at max this distance");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "flee_distance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Flee Distance", "Flee to this distance");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+static void rna_def_boidrule(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ /* data */
+ srna = RNA_def_struct(brna, "BoidRule", NULL);
+ RNA_def_struct_ui_text(srna, "Boid Rule", "");
+ RNA_def_struct_refine_func(srna, "rna_BoidRule_refine");
+ RNA_def_struct_path_func(srna, "rna_BoidRule_path");
+
+ /* strings */
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Boid rule name");
+ RNA_def_struct_name_property(srna, prop);
+
+ /* enums */
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, rna_enum_boidrule_type_items);
+ RNA_def_property_ui_text(prop, "Type", "");
+
+ /* flags */
+ prop = RNA_def_property(srna, "use_in_air", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", BOIDRULE_IN_AIR);
+ RNA_def_property_ui_text(prop, "In Air", "Use rule when boid is flying");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "use_on_land", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", BOIDRULE_ON_LAND);
+ RNA_def_property_ui_text(prop, "On Land", "Use rule when boid is on land");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ /*prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); */
+ /*RNA_def_property_boolean_sdna(prop, NULL, "mode", eModifierMode_Expanded); */
+ /*RNA_def_property_ui_text(prop, "Expanded", "Set modifier expanded in the user interface"); */
+
+ /* types */
+ rna_def_boidrule_goal(brna);
+ rna_def_boidrule_avoid(brna);
+ rna_def_boidrule_avoid_collision(brna);
+ rna_def_boidrule_follow_leader(brna);
+ rna_def_boidrule_average_speed(brna);
+ rna_def_boidrule_fight(brna);
+}
+
+static void rna_def_boidstate(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidState", NULL);
+ RNA_def_struct_ui_text(srna, "Boid State", "Boid state for boid physics");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Boid state name");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "ruleset_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, boidruleset_type_items);
+ RNA_def_property_ui_text(prop, "Rule Evaluation", "How the rules in the list are evaluated");
+
+ prop = RNA_def_property(srna, "rules", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "BoidRule");
+ RNA_def_property_ui_text(prop, "Boid Rules", "");
+
+ prop = RNA_def_property(srna, "active_boid_rule", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "BoidRule");
+ RNA_def_property_pointer_funcs(prop, "rna_BoidState_active_boid_rule_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Boid Rule", "");
+
+ prop = RNA_def_property(srna, "active_boid_rule_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_BoidState_active_boid_rule_index_get",
+ "rna_BoidState_active_boid_rule_index_set",
+ "rna_BoidState_active_boid_rule_index_range");
+ RNA_def_property_ui_text(prop, "Active Boid Rule Index", "");
+
+ prop = RNA_def_property(srna, "rule_fuzzy", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rule_fuzziness");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Rule Fuzziness", "");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "volume", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Volume", "");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "falloff", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_ui_text(prop, "Falloff", "");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+static void rna_def_boid_settings(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BoidSettings", NULL);
+ RNA_def_struct_path_func(srna, "rna_BoidSettings_path");
+ RNA_def_struct_ui_text(srna, "Boid Settings", "Settings for boid physics");
+
+ prop = RNA_def_property(srna, "land_smooth", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "landing_smoothness");
+ RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_ui_text(prop, "Landing Smoothness", "How smoothly the boids land");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "bank", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "banking");
+ RNA_def_property_range(prop, 0.0, 2.0);
+ RNA_def_property_ui_text(prop, "Banking", "Amount of rotation around velocity vector on turns");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "pitch", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "pitch");
+ RNA_def_property_range(prop, 0.0, 2.0);
+ RNA_def_property_ui_text(prop, "Pitch", "Amount of rotation around side vector");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "height", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 2.0);
+ RNA_def_property_ui_text(prop, "Height", "Boid height relative to particle size");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ /* states */
+ prop = RNA_def_property(srna, "states", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "BoidState");
+ RNA_def_property_ui_text(prop, "Boid States", "");
+
+ prop = RNA_def_property(srna, "active_boid_state", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "BoidRule");
+ RNA_def_property_pointer_funcs(prop, "rna_BoidSettings_active_boid_state_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Boid Rule", "");
+
+ prop = RNA_def_property(srna, "active_boid_state_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_BoidSettings_active_boid_state_index_get",
+ "rna_BoidSettings_active_boid_state_index_set",
+ "rna_BoidSettings_active_boid_state_index_range");
+ RNA_def_property_ui_text(prop, "Active Boid State Index", "");
+
+ /* character properties */
+ prop = RNA_def_property(srna, "health", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Health", "Initial boid health when born");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Strength", "Maximum caused damage on attack per second");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "aggression", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Aggression", "Boid will fight this times stronger enemy");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "accuracy", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Accuracy", "Accuracy of attack");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "range", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Range", "Maximum distance from which a boid can attack");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ /* physical properties */
+ prop = RNA_def_property(srna, "air_speed_min", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "air_min_speed");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Min Air Speed", "Minimum speed in air (relative to maximum speed)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "air_speed_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "air_max_speed");
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Max Air Speed", "Maximum speed in air");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "air_acc_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "air_max_acc");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Max Air Acceleration", "Maximum acceleration in air (relative to maximum speed)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "air_ave_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "air_max_ave");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Max Air Angular Velocity",
+ "Maximum angular velocity in air (relative to 180 degrees)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "air_personal_space", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_ui_text(prop, "Air Personal Space", "Radius of boids personal space in air (% of particle size)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "land_jump_speed", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Jump Speed", "Maximum speed for jumping");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "land_speed_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "land_max_speed");
+ RNA_def_property_range(prop, 0.0, 100.0);
+ RNA_def_property_ui_text(prop, "Max Land Speed", "Maximum speed on land");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "land_acc_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "land_max_acc");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Max Land Acceleration",
+ "Maximum acceleration on land (relative to maximum speed)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "land_ave_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "land_max_ave");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Max Land Angular Velocity",
+ "Maximum angular velocity on land (relative to 180 degrees)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "land_personal_space", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_ui_text(prop, "Land Personal Space",
+ "Radius of boids personal space on land (% of particle size)");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "land_stick_force", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 1000.0);
+ RNA_def_property_ui_text(prop, "Land Stick Force", "How strong a force must be to start effecting a boid on land");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ /* options */
+ prop = RNA_def_property(srna, "use_flight", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BOID_ALLOW_FLIGHT);
+ RNA_def_property_ui_text(prop, "Allow Flight", "Allow boids to move in air");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "use_land", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BOID_ALLOW_LAND);
+ RNA_def_property_ui_text(prop, "Allow Land", "Allow boids to move on land");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+
+ prop = RNA_def_property(srna, "use_climb", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "options", BOID_ALLOW_CLIMB);
+ RNA_def_property_ui_text(prop, "Allow Climbing", "Allow boids to climb goal objects");
+ RNA_def_property_update(prop, 0, "rna_Boids_reset");
+}
+
+void RNA_def_boid(BlenderRNA *brna)
+{
+ rna_def_boidrule(brna);
+ rna_def_boidstate(brna);
+ rna_def_boid_settings(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index b8051c4d0a2..d3cd3d12c4d 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -47,6 +47,7 @@
#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_sequence_types.h"
#include "MEM_guardedalloc.h"
@@ -345,6 +346,13 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *
WM_main_add_notifier(NC_LINESTYLE, linestyle);
break;
}
+ case ID_PA:
+ {
+ ParticleSettings *part = ptr->id.data;
+
+ DAG_id_tag_update(&part->id, OB_RECALC_DATA | PSYS_RECALC_REDO);
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, part);
+ }
default:
break;
}
diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c
index 1021aa60654..d7a679e9702 100644
--- a/source/blender/makesrna/intern/rna_context.c
+++ b/source/blender/makesrna/intern/rna_context.c
@@ -147,6 +147,7 @@ void RNA_def_context(BlenderRNA *brna)
{CTX_MODE_PAINT_WEIGHT, "PAINT_WEIGHT", 0, "Weight Paint", ""},
{CTX_MODE_PAINT_VERTEX, "PAINT_VERTEX", 0, "Vertex Paint", ""},
{CTX_MODE_PAINT_TEXTURE, "PAINT_TEXTURE", 0, "Texture Paint", ""},
+ {CTX_MODE_PARTICLE, "PARTICLE", 0, "Particle", ""},
{CTX_MODE_OBJECT, "OBJECT", 0, "Object", ""},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_dynamicpaint.c b/source/blender/makesrna/intern/rna_dynamicpaint.c
index 4cf6b027b06..4bb7f3a9ffd 100644
--- a/source/blender/makesrna/intern/rna_dynamicpaint.c
+++ b/source/blender/makesrna/intern/rna_dynamicpaint.c
@@ -56,6 +56,7 @@ EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[] = {
#include "BKE_context.h"
#include "BKE_depsgraph.h"
+#include "BKE_particle.h"
static char *rna_DynamicPaintCanvasSettings_path(PointerRNA *ptr)
@@ -100,6 +101,11 @@ static void rna_DynamicPaint_redoModifier(Main *UNUSED(bmain), Scene *UNUSED(sce
DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
}
+static void rna_DynamicPaintSurfaces_updateFrames(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ dynamicPaint_cacheUpdateFrames((DynamicPaintSurface *)ptr->data);
+}
+
static void rna_DynamicPaintSurface_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
{
dynamicPaint_resetSurface(scene, (DynamicPaintSurface *)ptr->data);
@@ -178,20 +184,20 @@ static void rna_DynamicPaint_surfaces_begin(CollectionPropertyIterator *iter, Po
rna_iterator_listbase_begin(iter, &canvas->surfaces, NULL);
}
-static int rna_Surface_active_index_get(PointerRNA *ptr)
+static int rna_Surface_active_point_index_get(PointerRNA *ptr)
{
DynamicPaintCanvasSettings *canvas = (DynamicPaintCanvasSettings *)ptr->data;
return canvas->active_sur;
}
-static void rna_Surface_active_index_set(struct PointerRNA *ptr, int value)
+static void rna_Surface_active_point_index_set(struct PointerRNA *ptr, int value)
{
DynamicPaintCanvasSettings *canvas = (DynamicPaintCanvasSettings *)ptr->data;
canvas->active_sur = value;
return;
}
-static void rna_Surface_active_range(PointerRNA *ptr, int *min, int *max,
+static void rna_Surface_active_point_range(PointerRNA *ptr, int *min, int *max,
int *UNUSED(softmin), int *UNUSED(softmax))
{
DynamicPaintCanvasSettings *canvas = (DynamicPaintCanvasSettings *)ptr->data;
@@ -216,7 +222,7 @@ static void rna_DynamicPaint_uvlayer_set(PointerRNA *ptr, const char *value)
}
}
-/* is cache used */
+/* is point cache used */
static int rna_DynamicPaint_is_cache_user_get(PointerRNA *ptr)
{
DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data;
@@ -305,9 +311,9 @@ static void rna_def_canvas_surfaces(BlenderRNA *brna, PropertyRNA *cprop)
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_int_funcs(prop, "rna_Surface_active_index_get", "rna_Surface_active_index_set",
- "rna_Surface_active_range");
- RNA_def_property_ui_text(prop, "Active Surface Index", "");
+ RNA_def_property_int_funcs(prop, "rna_Surface_active_point_index_get", "rna_Surface_active_point_index_set",
+ "rna_Surface_active_point_range");
+ RNA_def_property_ui_text(prop, "Active Point Cache Index", "");
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "DynamicPaintSurface");
@@ -467,7 +473,7 @@ static void rna_def_canvas_surface(BlenderRNA *brna)
RNA_def_property_range(prop, 1.0, MAXFRAMEF);
RNA_def_property_ui_range(prop, 1.0, 9999, 1, -1);
RNA_def_property_ui_text(prop, "Start Frame", "Simulation start frame");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaintSurfaces_updateFrames");
prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "end_frame");
@@ -475,7 +481,7 @@ static void rna_def_canvas_surface(BlenderRNA *brna)
RNA_def_property_range(prop, 1.0, MAXFRAMEF);
RNA_def_property_ui_range(prop, 1.0, 9999.0, 1, -1);
RNA_def_property_ui_text(prop, "End Frame", "Simulation end frame");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaintSurfaces_updateFrames");
prop = RNA_def_property(srna, "frame_substeps", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "substeps");
@@ -716,6 +722,13 @@ static void rna_def_canvas_surface(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_WAVE_OPEN_BORDERS);
RNA_def_property_ui_text(prop, "Open Borders", "Pass waves through mesh edges");
+
+ /* cache */
+ prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "pointcache");
+ RNA_def_property_ui_text(prop, "Point Cache", "");
+
/* is cache used */
prop = RNA_def_property(srna, "is_cache_user", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_DynamicPaint_is_cache_user_get", NULL);
@@ -942,6 +955,38 @@ static void rna_def_dynamic_paint_brush_settings(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_NEGATE_VOLUME);
RNA_def_property_ui_text(prop, "Negate Volume", "Negate influence inside the volume");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaint_redoModifier");
+
+
+ /*
+ * Particle
+ */
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "psys");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Particle Systems", "The particle system to paint with");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaint_reset_dependency");
+
+
+ prop = RNA_def_property(srna, "use_particle_radius", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_PART_RAD);
+ RNA_def_property_ui_text(prop, "Use Particle Radius", "Use radius from particle settings");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaint_redoModifier");
+
+ prop = RNA_def_property(srna, "solid_radius", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "particle_radius");
+ RNA_def_property_range(prop, 0.01, 10.0);
+ RNA_def_property_ui_range(prop, 0.01, 2.0, 5, 3);
+ RNA_def_property_ui_text(prop, "Solid Radius", "Radius that will be painted solid");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaint_redoModifier");
+
+ prop = RNA_def_property(srna, "smooth_radius", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "particle_smooth");
+ RNA_def_property_range(prop, 0.0, 10.0);
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 5, -1);
+ RNA_def_property_ui_text(prop, "Smooth Radius", "Smooth falloff added after solid radius");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_DynamicPaint_redoModifier");
+
/*
* Color ramps
diff --git a/source/blender/makesrna/intern/rna_fluidsim.c b/source/blender/makesrna/intern/rna_fluidsim.c
index 06dec0998a5..091950a8e66 100644
--- a/source/blender/makesrna/intern/rna_fluidsim.c
+++ b/source/blender/makesrna/intern/rna_fluidsim.c
@@ -43,14 +43,16 @@
#include "MEM_guardedalloc.h"
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_particle_types.h"
#include "BKE_depsgraph.h"
#include "BKE_fluidsim.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
static StructRNA *rna_FluidSettings_refine(struct PointerRNA *ptr)
{
@@ -119,10 +121,54 @@ static void rna_FluidSettings_update_type(Main *bmain, Scene *scene, PointerRNA
{
Object *ob = (Object *)ptr->id.data;
FluidsimModifierData *fluidmd;
+ ParticleSystemModifierData *psmd;
+ ParticleSystem *psys, *next_psys;
+ ParticleSettings *part;
fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
fluidmd->fss->flag &= ~OB_FLUIDSIM_REVERSE; /* clear flag */
+ /* remove fluidsim particle system */
+ if (fluidmd->fss->type & OB_FLUIDSIM_PARTICLE) {
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ if (psys->part->type == PART_FLUID)
+ break;
+
+ if (ob->type == OB_MESH && !psys) {
+ /* add particle system */
+ part = psys_new_settings("ParticleSettings", bmain);
+ psys = MEM_callocN(sizeof(ParticleSystem), "particle_system");
+
+ part->type = PART_FLUID;
+ psys->part = part;
+ psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
+ BLI_strncpy(psys->name, "FluidParticles", sizeof(psys->name));
+ BLI_addtail(&ob->particlesystem, psys);
+
+ /* add modifier */
+ psmd = (ParticleSystemModifierData *)modifier_new(eModifierType_ParticleSystem);
+ BLI_strncpy(psmd->modifier.name, "FluidParticleSystem", sizeof(psmd->modifier.name));
+ psmd->psys = psys;
+ BLI_addtail(&ob->modifiers, psmd);
+ modifier_unique_name(&ob->modifiers, (ModifierData *)psmd);
+ }
+ }
+ else {
+ for (psys = ob->particlesystem.first; psys; psys = next_psys) {
+ next_psys = psys->next;
+ if (psys->part->type == PART_FLUID) {
+ /* clear modifier */
+ psmd = psys_get_modifier(ob, psys);
+ BLI_remlink(&ob->modifiers, psmd);
+ modifier_free((ModifierData *)psmd);
+
+ /* clear particle system */
+ BLI_remlink(&ob->particlesystem, psys);
+ psys_free(ob, psys);
+ }
+ }
+ }
+
rna_fluid_update(bmain, scene, ptr);
}
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index c406aa987e5..76455adbc78 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -164,6 +164,7 @@ void RNA_def_object(struct BlenderRNA *brna);
void RNA_def_object_force(struct BlenderRNA *brna);
void RNA_def_packedfile(struct BlenderRNA *brna);
void RNA_def_palette(struct BlenderRNA *brna);
+void RNA_def_particle(struct BlenderRNA *brna);
void RNA_def_pose(struct BlenderRNA *brna);
void RNA_def_render(struct BlenderRNA *brna);
void RNA_def_rigidbody(struct BlenderRNA *brna);
@@ -326,6 +327,7 @@ void RNA_def_main_speakers(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_sounds(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_armatures(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop);
+void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop);
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index d5d6f609a03..94687b6fd46 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -239,6 +239,12 @@ static void rna_Main_brush_begin(CollectionPropertyIterator *iter, PointerRNA *p
rna_iterator_listbase_begin(iter, &bmain->brush, NULL);
}
+static void rna_Main_particle_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Main *bmain = (Main *)ptr->data;
+ rna_iterator_listbase_begin(iter, &bmain->particle, NULL);
+}
+
static void rna_Main_palettes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Main *bmain = (Main *)ptr->data;
@@ -354,6 +360,7 @@ void RNA_def_main(BlenderRNA *brna)
{"sounds", "Sound", "rna_Main_sound_begin", "Sounds", "Sound data-blocks", RNA_def_main_sounds},
{"armatures", "Armature", "rna_Main_armature_begin", "Armatures", "Armature data-blocks", RNA_def_main_armatures},
{"actions", "Action", "rna_Main_action_begin", "Actions", "Action data-blocks", RNA_def_main_actions},
+ {"particles", "ParticleSettings", "rna_Main_particle_begin", "Particles", "Particle data-blocks", RNA_def_main_particles},
{"palettes", "Palette", "rna_Main_palettes_begin", "Palettes", "Palette data-blocks", RNA_def_main_palettes},
{"grease_pencil", "GreasePencil", "rna_Main_gpencil_begin", "Grease Pencil", "Grease Pencil data-blocks", RNA_def_main_gpencil},
{"movieclips", "MovieClip", "rna_Main_movieclips_begin", "Movie Clips", "Movie Clip data-blocks", RNA_def_main_movieclips},
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index d8169e529aa..673f7acbd6a 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -74,6 +74,7 @@
#include "BKE_lattice.h"
#include "BKE_mball.h"
#include "BKE_world.h"
+#include "BKE_particle.h"
#include "BKE_paint.h"
#include "BKE_font.h"
#include "BKE_node.h"
@@ -99,6 +100,7 @@
#include "DNA_lattice_types.h"
#include "DNA_meta_types.h"
#include "DNA_world_types.h"
+#include "DNA_particle_types.h"
#include "DNA_vfont_types.h"
#include "DNA_node_types.h"
#include "DNA_movieclip_types.h"
@@ -442,6 +444,13 @@ static bAction *rna_Main_actions_new(Main *bmain, const char *name)
return act;
}
+static ParticleSettings *rna_Main_particles_new(Main *bmain, const char *name)
+{
+ ParticleSettings *part = psys_new_settings(name, bmain);
+ id_us_min(&part->id);
+ return part;
+}
+
static Palette *rna_Main_palettes_new(Main *bmain, const char *name)
{
Palette *palette = BKE_palette_add(bmain, name);
@@ -520,6 +529,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(speakers, speaker, ID_SPK)
RNA_MAIN_ID_TAG_FUNCS_DEF(sounds, sound, ID_SO)
RNA_MAIN_ID_TAG_FUNCS_DEF(armatures, armature, ID_AR)
RNA_MAIN_ID_TAG_FUNCS_DEF(actions, action, ID_AC)
+RNA_MAIN_ID_TAG_FUNCS_DEF(particles, particle, ID_PA)
RNA_MAIN_ID_TAG_FUNCS_DEF(palettes, palettes, ID_PAL)
RNA_MAIN_ID_TAG_FUNCS_DEF(gpencil, gpencil, ID_GD)
RNA_MAIN_ID_TAG_FUNCS_DEF(movieclips, movieclip, ID_MC)
@@ -1468,6 +1478,42 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_Main_actions_is_updated_get", NULL);
}
+void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+ PropertyRNA *prop;
+
+ RNA_def_property_srna(cprop, "BlendDataParticles");
+ srna = RNA_def_struct(brna, "BlendDataParticles", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Particle Settings", "Collection of particle settings");
+
+ func = RNA_def_function(srna, "new", "rna_Main_particles_new");
+ RNA_def_function_ui_description(func, "Add a new particle settings instance to the main database");
+ parm = RNA_def_string(func, "name", "ParticleSettings", 0, "", "New name for the data-block");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ /* return type */
+ parm = RNA_def_pointer(func, "particle", "ParticleSettings", "", "New particle settings data-block");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove a particle settings instance from the current blendfile");
+ parm = RNA_def_pointer(func, "particle", "ParticleSettings", "", "Particle Settings to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_boolean(func, "do_unlink", true, "", "Unlink all usages of those particle settings before deleting them");
+
+ func = RNA_def_function(srna, "tag", "rna_Main_particles_tag");
+ parm = RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_Main_particles_is_updated_get", NULL);
+}
void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop)
{
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 5d78df105da..c4f0db38a16 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -114,6 +114,8 @@ EnumPropertyItem rna_enum_object_modifier_type_items[] = {
{eModifierType_Explode, "EXPLODE", ICON_MOD_EXPLODE, "Explode", ""},
{eModifierType_Fluidsim, "FLUID_SIMULATION", ICON_MOD_FLUIDSIM, "Fluid Simulation", ""},
{eModifierType_Ocean, "OCEAN", ICON_MOD_OCEAN, "Ocean", ""},
+ {eModifierType_ParticleInstance, "PARTICLE_INSTANCE", ICON_MOD_PARTICLES, "Particle Instance", ""},
+ {eModifierType_ParticleSystem, "PARTICLE_SYSTEM", ICON_MOD_PARTICLES, "Particle System", ""},
{eModifierType_Smoke, "SMOKE", ICON_MOD_SMOKE, "Smoke", ""},
{eModifierType_Softbody, "SOFT_BODY", ICON_MOD_SOFT, "Soft Body", ""},
{eModifierType_Surface, "SURFACE", ICON_MOD_PHYSICS, "Surface", ""},
@@ -277,6 +279,7 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
#ifdef RNA_RUNTIME
+#include "DNA_particle_types.h"
#include "DNA_curve_types.h"
#include "DNA_smoke_types.h"
@@ -286,6 +289,7 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
#include "BKE_library.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#ifdef WITH_ALEMBIC
# include "ABC_alembic.h"
@@ -338,6 +342,10 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr)
return &RNA_CastModifier;
case eModifierType_MeshDeform:
return &RNA_MeshDeformModifier;
+ case eModifierType_ParticleSystem:
+ return &RNA_ParticleSystemModifier;
+ case eModifierType_ParticleInstance:
+ return &RNA_ParticleInstanceModifier;
case eModifierType_Explode:
return &RNA_ExplodeModifier;
case eModifierType_Cloth:
@@ -699,6 +707,12 @@ static PointerRNA rna_SoftBodyModifier_settings_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_SoftBodySettings, ob->soft);
}
+static PointerRNA rna_SoftBodyModifier_point_cache_get(PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ return rna_pointer_inherit_refine(ptr, &RNA_PointCache, ob->soft->pointcache);
+}
+
static PointerRNA rna_CollisionModifier_settings_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->id.data;
@@ -1886,6 +1900,12 @@ static void rna_def_modifier_softbody(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "SoftBodySettings");
RNA_def_property_pointer_funcs(prop, "rna_SoftBodyModifier_settings_get", NULL, NULL, NULL);
RNA_def_property_ui_text(prop, "Soft Body Settings", "");
+
+ prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "PointCache");
+ RNA_def_property_pointer_funcs(prop, "rna_SoftBodyModifier_point_cache_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Soft Body Point Cache", "");
}
static void rna_def_modifier_boolean(BlenderRNA *brna)
@@ -2557,6 +2577,104 @@ static void rna_def_modifier_meshdeform(BlenderRNA *brna)
#endif
}
+static void rna_def_modifier_particlesystem(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ParticleSystemModifier", "Modifier");
+ RNA_def_struct_ui_text(srna, "ParticleSystem Modifier", "Particle system simulation modifier");
+ RNA_def_struct_sdna(srna, "ParticleSystemModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_PARTICLES);
+
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "psys");
+ RNA_def_property_ui_text(prop, "Particle System", "Particle System that this modifier controls");
+}
+
+static void rna_def_modifier_particleinstance(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ParticleInstanceModifier", "Modifier");
+ RNA_def_struct_ui_text(srna, "ParticleInstance Modifier", "Particle system instancing modifier");
+ RNA_def_struct_sdna(srna, "ParticleInstanceModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_PARTICLES);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "ob");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Mesh_object_poll");
+ RNA_def_property_ui_text(prop, "Object", "Object that has the particle system");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+
+ prop = RNA_def_property(srna, "particle_system_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "psys");
+ RNA_def_property_range(prop, 1, 10);
+ RNA_def_property_ui_text(prop, "Particle System Number", "");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "axis");
+ RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items);
+ RNA_def_property_ui_text(prop, "Axis", "Pole axis for rotation");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Parents);
+ RNA_def_property_ui_text(prop, "Normal", "Create instances from normal particles");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_children", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Children);
+ RNA_def_property_ui_text(prop, "Children", "Create instances from child particles");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_path", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Path);
+ RNA_def_property_ui_text(prop, "Path", "Create instances along particle paths");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "show_unborn", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Unborn);
+ RNA_def_property_ui_text(prop, "Unborn", "Show instances when particles are unborn");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "show_alive", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Alive);
+ RNA_def_property_ui_text(prop, "Alive", "Show instances when particles are alive");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "show_dead", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Dead);
+ RNA_def_property_ui_text(prop, "Dead", "Show instances when particles are dead");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_preserve_shape", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_KeepShape);
+ RNA_def_property_ui_text(prop, "Keep Shape", "Don't stretch the object");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_size", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_UseSize);
+ RNA_def_property_ui_text(prop, "Size", "Use particle size to scale the instances");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "position");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Position", "Position along path");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "random_position", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "random_position");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Random Position", "Randomize position along path");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+}
+
static void rna_def_modifier_explode(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2633,6 +2751,10 @@ static void rna_def_modifier_cloth(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "ClothSolverResult");
RNA_def_property_pointer_sdna(prop, NULL, "solver_result");
RNA_def_property_ui_text(prop, "Solver Result", "");
+
+ prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_ui_text(prop, "Point Cache", "");
prop = RNA_def_property(srna, "hair_grid_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "hair_grid_min");
@@ -4665,6 +4787,8 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_correctivesmooth(brna);
rna_def_modifier_cast(brna);
rna_def_modifier_meshdeform(brna);
+ rna_def_modifier_particlesystem(brna);
+ rna_def_modifier_particleinstance(brna);
rna_def_modifier_explode(brna);
rna_def_modifier_cloth(brna);
rna_def_modifier_collision(brna);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 5f322485ca5..a9e78428212 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -38,6 +38,7 @@
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_text_types.h"
#include "DNA_texture_types.h"
@@ -3012,6 +3013,36 @@ static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA
rna_Node_update(bmain, scene, ptr);
}
+static PointerRNA rna_ShaderNodePointDensity_psys_get(PointerRNA *ptr)
+{
+ bNode *node = ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = node->storage;
+ Object *ob = (Object *)node->id;
+ ParticleSystem *psys = NULL;
+ PointerRNA value;
+
+ if (ob && shader_point_density->particle_system) {
+ psys = BLI_findlink(&ob->particlesystem, shader_point_density->particle_system - 1);
+ }
+
+ RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_ShaderNodePointDensity_psys_set(PointerRNA *ptr, PointerRNA value)
+{
+ bNode *node = ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = node->storage;
+ Object *ob = (Object *)node->id;
+
+ if (ob && value.id.data == ob) {
+ shader_point_density->particle_system = BLI_findindex(&ob->particlesystem, value.data) + 1;
+ }
+ else {
+ shader_point_density->particle_system = 0;
+ }
+}
+
static int point_density_particle_color_source_from_shader(NodeShaderTexPointDensity *shader_point_density)
{
switch (shader_point_density->color_source) {
@@ -4061,6 +4092,14 @@ static void def_sh_tex_pointdensity(StructRNA *srna)
RNA_def_property_ui_text(prop, "Point Source", "Point data to use as renderable point density");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Particle System", "Particle System to render as points");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_pointer_funcs(prop, "rna_ShaderNodePointDensity_psys_get",
+ "rna_ShaderNodePointDensity_psys_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 1, 32768);
RNA_def_property_ui_text(prop, "Resolution", "Resolution used by the texture holding the point density");
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 4f82fa0d1ec..0cffba47f16 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -68,6 +68,7 @@ EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
{OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
{OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
+ {OB_MODE_PARTICLE_EDIT, "PARTICLE_EDIT", ICON_PARTICLEMODE, "Particle Edit", ""},
{OB_MODE_GPENCIL, "GPENCIL_EDIT", ICON_GREASEPENCIL, "Edit Strokes", "Edit Grease Pencil Strokes"},
{0, NULL, 0, NULL, NULL}
};
@@ -187,10 +188,12 @@ EnumPropertyItem rna_enum_object_axis_items[] = {
#include "BKE_object.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_deform.h"
#include "ED_object.h"
+#include "ED_particle.h"
#include "ED_curve.h"
#include "ED_lattice.h"
@@ -724,6 +727,34 @@ static int rna_Object_active_material_editable(PointerRNA *ptr, const char **UNU
return is_editable ? PROP_EDITABLE : 0;
}
+
+static void rna_Object_active_particle_system_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ Object *ob = (Object *)ptr->id.data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&ob->particlesystem) - 1);
+}
+
+static int rna_Object_active_particle_system_index_get(PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ return psys_get_current_num(ob);
+}
+
+static void rna_Object_active_particle_system_index_set(PointerRNA *ptr, int value)
+{
+ Object *ob = (Object *)ptr->id.data;
+ psys_set_current_num(ob, value);
+}
+
+static void rna_Object_particle_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+
+ PE_current_changed(scene, ob);
+}
+
/* rotation - axis-angle */
static void rna_Object_rotation_axis_angle_get(PointerRNA *ptr, float *value)
{
@@ -1044,6 +1075,13 @@ static void rna_GameObjectSettings_physics_type_set(PointerRNA *ptr, int value)
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ptr->id.data);
}
+static PointerRNA rna_Object_active_particle_system_get(PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystem *psys = psys_get_current(ob);
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleSystem, psys);
+}
+
static PointerRNA rna_Object_game_settings_get(PointerRNA *ptr)
{
return rna_pointer_inherit_refine(ptr, &RNA_GameObjectSettings, ptr->id.data);
@@ -1989,6 +2027,37 @@ static void rna_def_object_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_ui_description(func, "Remove all modifiers from the object");
}
+/* object.particle_systems */
+static void rna_def_object_particle_systems(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+
+ PropertyRNA *prop;
+
+ /* FunctionRNA *func; */
+ /* PropertyRNA *parm; */
+
+ RNA_def_property_srna(cprop, "ParticleSystems");
+ srna = RNA_def_struct(brna, "ParticleSystems", NULL);
+ RNA_def_struct_sdna(srna, "Object");
+ RNA_def_struct_ui_text(srna, "Particle Systems", "Collection of particle systems");
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_pointer_funcs(prop, "rna_Object_active_particle_system_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Particle System", "Active particle system being displayed");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_funcs(prop, "rna_Object_active_particle_system_index_get",
+ "rna_Object_active_particle_system_index_set",
+ "rna_Object_active_particle_system_index_range");
+ RNA_def_property_ui_text(prop, "Active Particle System Index", "Index of active particle system slot");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_particle_update");
+}
+
+
/* object.vertex_groups */
static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop)
{
@@ -2518,6 +2587,13 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "SoftBodySettings");
RNA_def_property_ui_text(prop, "Soft Body Settings", "Settings for soft body simulation");
+ prop = RNA_def_property(srna, "particle_systems", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "particlesystem", NULL);
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_ui_text(prop, "Particle Systems", "Particle systems emitted from the object");
+ rna_def_object_particle_systems(brna, prop);
+
+
prop = RNA_def_property(srna, "rigid_body", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "rigidbody_object");
RNA_def_property_struct_type(prop, "RigidBodyObject");
@@ -2800,6 +2876,10 @@ static void rna_def_dupli_object(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Persistent ID", "Persistent identifier for inter-frame matching of objects with motion blur");
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Particle System", "Particle system that this dupli object was instanced from");
+
prop = RNA_def_property(srna, "orco", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Generated Coordinates", "Generated coordinates in parent object space");
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 06affc7787c..c680abe71a4 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -146,9 +146,54 @@ static Mesh *rna_Object_to_mesh(
return rna_Main_meshes_new_from_object(G.main, reports, sce, ob, apply_modifiers, settings, calc_tessface, calc_undeformed);
}
+/* mostly a copy from convertblender.c */
+static void dupli_render_particle_set(Scene *scene, Object *ob, int level, int enable)
+{
+ /* ugly function, but we need to set particle systems to their render
+ * settings before calling object_duplilist, to get render level duplis */
+ Group *group;
+ GroupObject *go;
+ ParticleSystem *psys;
+ DerivedMesh *dm;
+ float mat[4][4];
+
+ unit_m4(mat);
+
+ if (level >= MAX_DUPLI_RECUR)
+ return;
+
+ if (ob->transflag & OB_DUPLIPARTS) {
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (ELEM(psys->part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
+ if (enable)
+ psys_render_set(ob, psys, mat, mat, 1, 1, 0.f);
+ else
+ psys_render_restore(ob, psys);
+ }
+ }
+
+ if (enable) {
+ /* this is to make sure we get render level duplis in groups:
+ * the derivedmesh must be created before init_render_mesh,
+ * since object_duplilist does dupliparticles before that */
+ dm = mesh_create_derived_render(scene, ob, CD_MASK_BAREMESH | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL);
+ dm->release(dm);
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated;
+ }
+ }
+
+ if (ob->dup_group == NULL) return;
+ group = ob->dup_group;
+
+ for (go = group->gobject.first; go; go = go->next)
+ dupli_render_particle_set(scene, go->ob, level + 1, enable);
+}
/* When no longer needed, duplilist should be freed with Object.free_duplilist */
static void rna_Object_create_duplilist(Object *ob, ReportList *reports, Scene *sce, int settings)
{
+ bool for_render = (settings == DAG_EVAL_RENDER);
EvaluationContext eval_ctx;
DEG_evaluation_context_init(&eval_ctx, settings);
@@ -164,7 +209,11 @@ static void rna_Object_create_duplilist(Object *ob, ReportList *reports, Scene *
free_object_duplilist(ob->duplilist);
ob->duplilist = NULL;
}
+ if (for_render)
+ dupli_render_particle_set(sce, ob, 0, 1);
ob->duplilist = object_duplilist(&eval_ctx, sce, ob);
+ if (for_render)
+ dupli_render_particle_set(sce, ob, 0, 0);
/* ob->duplilist should now be freed with Object.free_duplilist */
}
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index ad927073871..1d89f7535c4 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -29,6 +29,7 @@
#include "DNA_cloth_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_smoke_types.h"
@@ -90,16 +91,233 @@ static EnumPropertyItem empty_vortex_shape_items[] = {
#include "MEM_guardedalloc.h"
-#include "DNA_dynamicpaint_types.h"
#include "DNA_modifier_types.h"
#include "DNA_texture_types.h"
#include "BKE_context.h"
#include "BKE_modifier.h"
+#include "BKE_pointcache.h"
#include "BKE_depsgraph.h"
#include "ED_object.h"
+static void rna_Cache_change(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ PointCache *cache = (PointCache *)ptr->data;
+ PTCacheID *pid = NULL;
+ ListBase pidlist;
+
+ if (!ob)
+ return;
+
+ cache->flag |= PTCACHE_OUTDATED;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache)
+ break;
+ }
+
+ if (pid) {
+ /* Just make sure this wasn't changed. */
+ if (pid->type == PTCACHE_TYPE_SMOKE_DOMAIN)
+ cache->step = 1;
+ BKE_ptcache_update_info(pid);
+ }
+
+ BLI_freelistN(&pidlist);
+}
+
+static void rna_Cache_toggle_disk_cache(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ PointCache *cache = (PointCache *)ptr->data;
+ PTCacheID *pid = NULL;
+ ListBase pidlist;
+
+ if (!ob)
+ return;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache)
+ break;
+ }
+
+ /* smoke can only use disk cache */
+ if (pid && pid->type != PTCACHE_TYPE_SMOKE_DOMAIN)
+ BKE_ptcache_toggle_disk_cache(pid);
+ else
+ cache->flag ^= PTCACHE_DISK_CACHE;
+
+ BLI_freelistN(&pidlist);
+}
+
+static void rna_Cache_idname_change(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ PointCache *cache = (PointCache *)ptr->data;
+ PTCacheID *pid = NULL, *pid2 = NULL;
+ ListBase pidlist;
+ bool use_new_name = true;
+
+ if (!ob)
+ return;
+
+ /* TODO: check for proper characters */
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ if (cache->flag & PTCACHE_EXTERNAL) {
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache)
+ break;
+ }
+
+ if (!pid)
+ return;
+
+ BKE_ptcache_load_external(pid);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_POINTCACHE, ob);
+ }
+ else {
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache)
+ pid2 = pid;
+ else if (cache->name[0] != '\0' && STREQ(cache->name, pid->cache->name)) {
+ /*TODO: report "name exists" to user */
+ BLI_strncpy(cache->name, cache->prev_name, sizeof(cache->name));
+ use_new_name = false;
+ }
+ }
+
+ if (use_new_name) {
+ BLI_filename_make_safe(cache->name);
+
+ if (pid2 && cache->flag & PTCACHE_DISK_CACHE) {
+ char old_name[80];
+ char new_name[80];
+
+ BLI_strncpy(old_name, cache->prev_name, sizeof(old_name));
+ BLI_strncpy(new_name, cache->name, sizeof(new_name));
+
+ BKE_ptcache_disk_cache_rename(pid2, old_name, new_name);
+ }
+
+ BLI_strncpy(cache->prev_name, cache->name, sizeof(cache->prev_name));
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+}
+
+static void rna_Cache_list_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ PointCache *cache = ptr->data;
+ ListBase lb;
+
+ while (cache->prev)
+ cache = cache->prev;
+
+ lb.first = cache;
+ lb.last = NULL; /* not used by listbase_begin */
+
+ rna_iterator_listbase_begin(iter, &lb, NULL);
+}
+static void rna_Cache_active_point_cache_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ Object *ob = ptr->id.data;
+ PointCache *cache = ptr->data;
+ PTCacheID *pid;
+ ListBase pidlist;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ *min = 0;
+ *max = 0;
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache) {
+ *max = max_ii(0, BLI_listbase_count(pid->ptcaches) - 1);
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+}
+
+static int rna_Cache_active_point_cache_index_get(PointerRNA *ptr)
+{
+ Object *ob = ptr->id.data;
+ PointCache *cache = ptr->data;
+ PTCacheID *pid;
+ ListBase pidlist;
+ int num = 0;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache) {
+ num = BLI_findindex(pid->ptcaches, cache);
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+
+ return num;
+}
+
+static void rna_Cache_active_point_cache_index_set(struct PointerRNA *ptr, int value)
+{
+ Object *ob = ptr->id.data;
+ PointCache *cache = ptr->data;
+ PTCacheID *pid;
+ ListBase pidlist;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache) {
+ *(pid->cache_ptr) = BLI_findlink(pid->ptcaches, value);
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+}
+
+static void rna_PointCache_frame_step_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ Object *ob = ptr->id.data;
+ PointCache *cache = ptr->data;
+ PTCacheID *pid;
+ ListBase pidlist;
+
+ *min = 1;
+ *max = 20;
+
+ BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
+
+ for (pid = pidlist.first; pid; pid = pid->next) {
+ if (pid->cache == cache) {
+ *max = pid->max_step;
+ break;
+ }
+ }
+
+ BLI_freelistN(&pidlist);
+}
+
static char *rna_CollisionSettings_path(PointerRNA *UNUSED(ptr))
{
/* both methods work ok, but return the shorter path */
@@ -259,25 +477,53 @@ static char *rna_SoftBodySettings_path(PointerRNA *ptr)
return BLI_sprintfN("modifiers[\"%s\"].settings", name_esc);
}
+static int particle_id_check(PointerRNA *ptr)
+{
+ ID *id = ptr->id.data;
+
+ return (GS(id->name) == ID_PA);
+}
+
static void rna_FieldSettings_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
- Object *ob = (Object *)ptr->id.data;
+ if (particle_id_check(ptr)) {
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ if (part->pd->forcefield != PFIELD_TEXTURE && part->pd->tex) {
+ id_us_min(&part->pd->tex->id);
+ part->pd->tex = NULL;
+ }
+
+ if (part->pd2 && part->pd2->forcefield != PFIELD_TEXTURE && part->pd2->tex) {
+ id_us_min(&part->pd2->tex->id);
+ part->pd2->tex = NULL;
+ }
+
+ DAG_id_tag_update(&part->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME | PSYS_RECALC_RESET);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
- if (ob->pd->forcefield != PFIELD_TEXTURE && ob->pd->tex) {
- id_us_min(&ob->pd->tex->id);
- ob->pd->tex = NULL;
}
+ else {
+ Object *ob = (Object *)ptr->id.data;
- DAG_id_tag_update(&ob->id, OB_RECALC_OB);
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+ if (ob->pd->forcefield != PFIELD_TEXTURE && ob->pd->tex) {
+ id_us_min(&ob->pd->tex->id);
+ ob->pd->tex = NULL;
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+ }
}
static void rna_FieldSettings_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- Object *ob = (Object *)ptr->id.data;
- ED_object_check_force_modifiers(bmain, scene, ob);
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
- WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
+ if (!particle_id_check(ptr)) {
+ Object *ob = (Object *)ptr->id.data;
+ ED_object_check_force_modifiers(bmain, scene, ob);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
+ }
}
static void rna_FieldSettings_type_set(PointerRNA *ptr, int value)
@@ -286,39 +532,46 @@ static void rna_FieldSettings_type_set(PointerRNA *ptr, int value)
part_deflect->forcefield = value;
- Object *ob = (Object *)ptr->id.data;
- ob->pd->forcefield = value;
- if (ELEM(value, PFIELD_WIND, PFIELD_VORTEX)) {
- ob->empty_drawtype = OB_SINGLE_ARROW;
- }
- else {
- ob->empty_drawtype = OB_PLAINAXES;
+ if (!particle_id_check(ptr)) {
+ Object *ob = (Object *)ptr->id.data;
+ ob->pd->forcefield = value;
+ if (ELEM(value, PFIELD_WIND, PFIELD_VORTEX)) {
+ ob->empty_drawtype = OB_SINGLE_ARROW;
+ }
+ else {
+ ob->empty_drawtype = OB_PLAINAXES;
+ }
}
}
static void rna_FieldSettings_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- Object *ob = (Object *)ptr->id.data;
+ if (particle_id_check(ptr)) {
+ DAG_id_tag_update((ID *)ptr->id.data, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME | PSYS_RECALC_RESET);
+ }
+ else {
+ Object *ob = (Object *)ptr->id.data;
- /* do this before scene sort, that one checks for CU_PATH */
+ /* do this before scene sort, that one checks for CU_PATH */
#if 0 /* XXX */
- if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE) {
- Curve *cu = ob->data;
- cu->flag |= (CU_PATH | CU_3D);
- do_curvebuts(B_CU3D); /* all curves too */
- }
+ if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE) {
+ Curve *cu = ob->data;
+ cu->flag |= (CU_PATH | CU_3D);
+ do_curvebuts(B_CU3D); /* all curves too */
+ }
#endif
- rna_FieldSettings_shape_update(bmain, scene, ptr);
+ rna_FieldSettings_shape_update(bmain, scene, ptr);
- DAG_relations_tag_update(bmain);
+ DAG_relations_tag_update(bmain);
- if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE)
- DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
- else
- DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+ if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE)
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
+ else
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB);
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+ }
}
static char *rna_FieldSettings_path(PointerRNA *ptr)
@@ -327,12 +580,22 @@ static char *rna_FieldSettings_path(PointerRNA *ptr)
/* Check through all possible places the settings can be to find the right one */
- /* object force field */
- Object *ob = (Object *)ptr->id.data;
-
- if (ob->pd == pd)
- return BLI_sprintfN("field");
-
+ if (particle_id_check(ptr)) {
+ /* particle system force field */
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ if (part->pd == pd)
+ return BLI_sprintfN("force_field_1");
+ else if (part->pd2 == pd)
+ return BLI_sprintfN("force_field_2");
+ }
+ else {
+ /* object force field */
+ Object *ob = (Object *)ptr->id.data;
+
+ if (ob->pd == pd)
+ return BLI_sprintfN("field");
+ }
return NULL;
}
@@ -340,15 +603,25 @@ static void rna_EffectorWeight_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
{
ID *id = ptr->id.data;
- DAG_id_tag_update(id, OB_RECALC_DATA);
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
+ if (id && GS(id->name) == ID_SCE) {
+ Scene *scene = (Scene *)id;
+ Base *base;
+
+ for (base = scene->base.first; base; base = base->next) {
+ BKE_ptcache_object_reset(scene, base->object, PTCACHE_RESET_DEPSGRAPH);
+ }
+ }
+ else {
+ DAG_id_tag_update(id, OB_RECALC_DATA | PSYS_RECALC_RESET);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
+ }
}
static void rna_EffectorWeight_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
DAG_relations_tag_update(bmain);
- DAG_id_tag_update((ID *)ptr->id.data, OB_RECALC_DATA);
+ DAG_id_tag_update((ID *)ptr->id.data, OB_RECALC_DATA | PSYS_RECALC_RESET);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
}
@@ -358,59 +631,68 @@ static char *rna_EffectorWeight_path(PointerRNA *ptr)
EffectorWeights *ew = (EffectorWeights *)ptr->data;
/* Check through all possible places the settings can be to find the right one */
- Object *ob = (Object *)ptr->id.data;
- ModifierData *md;
-
- /* check softbody modifier */
- md = (ModifierData *)modifiers_findByType(ob, eModifierType_Softbody);
- if (md) {
- /* no pointer from modifier data to actual softbody storage, would be good to add */
- if (ob->soft->effector_weights == ew) {
- char name_esc[sizeof(md->name) * 2];
- BLI_strescape(name_esc, md->name, sizeof(name_esc));
- return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
- }
+ if (particle_id_check(ptr)) {
+ /* particle effector weights */
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ if (part->effector_weights == ew)
+ return BLI_sprintfN("effector_weights");
}
-
- /* check cloth modifier */
- md = (ModifierData *)modifiers_findByType(ob, eModifierType_Cloth);
- if (md) {
- ClothModifierData *cmd = (ClothModifierData *)md;
- if (cmd->sim_parms->effector_weights == ew) {
- char name_esc[sizeof(md->name) * 2];
- BLI_strescape(name_esc, md->name, sizeof(name_esc));
- return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
+ else {
+ Object *ob = (Object *)ptr->id.data;
+ ModifierData *md;
+
+ /* check softbody modifier */
+ md = (ModifierData *)modifiers_findByType(ob, eModifierType_Softbody);
+ if (md) {
+ /* no pointer from modifier data to actual softbody storage, would be good to add */
+ if (ob->soft->effector_weights == ew) {
+ char name_esc[sizeof(md->name) * 2];
+ BLI_strescape(name_esc, md->name, sizeof(name_esc));
+ return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
+ }
}
- }
-
- /* check smoke modifier */
- md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
- if (md) {
- SmokeModifierData *smd = (SmokeModifierData *)md;
- if (smd->domain->effector_weights == ew) {
- char name_esc[sizeof(md->name) * 2];
- BLI_strescape(name_esc, md->name, sizeof(name_esc));
- return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
+
+ /* check cloth modifier */
+ md = (ModifierData *)modifiers_findByType(ob, eModifierType_Cloth);
+ if (md) {
+ ClothModifierData *cmd = (ClothModifierData *)md;
+ if (cmd->sim_parms->effector_weights == ew) {
+ char name_esc[sizeof(md->name) * 2];
+ BLI_strescape(name_esc, md->name, sizeof(name_esc));
+ return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
+ }
+ }
+
+ /* check smoke modifier */
+ md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ if (md) {
+ SmokeModifierData *smd = (SmokeModifierData *)md;
+ if (smd->domain->effector_weights == ew) {
+ char name_esc[sizeof(md->name) * 2];
+ BLI_strescape(name_esc, md->name, sizeof(name_esc));
+ return BLI_sprintfN("modifiers[\"%s\"].settings.effector_weights", name_esc);
+ }
}
- }
- /* check dynamic paint modifier */
- md = (ModifierData *)modifiers_findByType(ob, eModifierType_DynamicPaint);
- if (md) {
- DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
+ /* check dynamic paint modifier */
+ md = (ModifierData *)modifiers_findByType(ob, eModifierType_DynamicPaint);
+ if (md) {
+ DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
- if (pmd->canvas) {
- DynamicPaintSurface *surface = pmd->canvas->surfaces.first;
+ if (pmd->canvas) {
+ DynamicPaintSurface *surface = pmd->canvas->surfaces.first;
- for (; surface; surface = surface->next) {
- if (surface->effector_weights == ew) {
- char name_esc[sizeof(md->name) * 2];
- char name_esc_surface[sizeof(surface->name) * 2];
+ for (; surface; surface = surface->next) {
+ if (surface->effector_weights == ew) {
+ char name_esc[sizeof(md->name) * 2];
+ char name_esc_surface[sizeof(surface->name) * 2];
- BLI_strescape(name_esc, md->name, sizeof(name_esc));
- BLI_strescape(name_esc_surface, surface->name, sizeof(name_esc_surface));
- return BLI_sprintfN("modifiers[\"%s\"].canvas_settings.canvas_surfaces[\"%s\"]"
- ".effector_weights", name_esc, name_esc_surface);
+ BLI_strescape(name_esc, md->name, sizeof(name_esc));
+ BLI_strescape(name_esc_surface, surface->name, sizeof(name_esc_surface));
+ return BLI_sprintfN("modifiers[\"%s\"].canvas_settings.canvas_surfaces[\"%s\"]"
+ ".effector_weights", name_esc, name_esc_surface);
+ }
}
}
}
@@ -459,6 +741,9 @@ static EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), PointerRN
{
Object *ob = NULL;
+ if (particle_id_check(ptr))
+ return empty_shape_items;
+
ob = (Object *)ptr->id.data;
if (ob->type == OB_CURVE) {
@@ -483,6 +768,140 @@ static EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), PointerRN
#else
+/* ptcache.point_caches */
+static void rna_def_ptcache_point_caches(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ /* FunctionRNA *func; */
+ /* PropertyRNA *parm; */
+
+ RNA_def_property_srna(cprop, "PointCaches");
+ srna = RNA_def_struct(brna, "PointCaches", NULL);
+ RNA_def_struct_sdna(srna, "PointCache");
+ RNA_def_struct_ui_text(srna, "Point Caches", "Collection of point caches");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_Cache_active_point_cache_index_get",
+ "rna_Cache_active_point_cache_index_set",
+ "rna_Cache_active_point_cache_index_range");
+ RNA_def_property_ui_text(prop, "Active Point Cache Index", "");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_change");
+}
+
+static void rna_def_pointcache(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem point_cache_compress_items[] = {
+ {PTCACHE_COMPRESS_NO, "NO", 0, "No", "No compression"},
+ {PTCACHE_COMPRESS_LZO, "LIGHT", 0, "Light", "Fast but not so effective compression"},
+ {PTCACHE_COMPRESS_LZMA, "HEAVY", 0, "Heavy", "Effective but slow compression"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "PointCache", NULL);
+ RNA_def_struct_ui_text(srna, "Point Cache", "Point cache for physics simulations");
+ RNA_def_struct_ui_icon(srna, ICON_PHYSICS);
+
+ prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
+ RNA_def_property_int_sdna(prop, NULL, "startframe");
+ RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
+ RNA_def_property_ui_range(prop, 1, MAXFRAME, 1, 1);
+ RNA_def_property_ui_text(prop, "Start", "Frame on which the simulation starts");
+
+ prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_TIME);
+ RNA_def_property_int_sdna(prop, NULL, "endframe");
+ RNA_def_property_range(prop, 1, MAXFRAME);
+ RNA_def_property_ui_text(prop, "End", "Frame on which the simulation stops");
+
+ prop = RNA_def_property(srna, "frame_step", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "step");
+ RNA_def_property_range(prop, 1, 20);
+ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_PointCache_frame_step_range");
+ RNA_def_property_ui_text(prop, "Cache Step", "Number of frames between cached frames");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_change");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "index");
+ RNA_def_property_range(prop, -1, 100);
+ RNA_def_property_ui_text(prop, "Cache Index", "Index number of cache files");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
+
+ prop = RNA_def_property(srna, "compression", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, point_cache_compress_items);
+ RNA_def_property_ui_text(prop, "Cache Compression", "Compression method to be used");
+
+ /* flags */
+ prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_BAKED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ prop = RNA_def_property(srna, "is_baking", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_BAKING);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ prop = RNA_def_property(srna, "use_disk_cache", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_DISK_CACHE);
+ RNA_def_property_ui_text(prop, "Disk Cache", "Save cache files to disk (.blend file must be saved first)");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_toggle_disk_cache");
+
+ prop = RNA_def_property(srna, "is_outdated", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_OUTDATED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Cache is outdated", "");
+
+ prop = RNA_def_property(srna, "is_frame_skip", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_FRAMES_SKIPPED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_ui_text(prop, "Name", "Cache name");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_DIRPATH);
+ RNA_def_property_string_sdna(prop, NULL, "path");
+ RNA_def_property_ui_text(prop, "File Path", "Cache file path");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
+
+ /* removed, see PTCACHE_QUICK_CACHE */
+#if 0
+ prop = RNA_def_property(srna, "use_quick_cache", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_QUICK_CACHE);
+ RNA_def_property_ui_text(prop, "Quick Cache", "Update simulation with cache steps");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_change");
+#endif
+
+ prop = RNA_def_property(srna, "info", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "info");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Cache Info", "Info on current cache status");
+
+ prop = RNA_def_property(srna, "use_external", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_EXTERNAL);
+ RNA_def_property_ui_text(prop, "External", "Read cache from an external location");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
+
+ prop = RNA_def_property(srna, "use_library_path", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", PTCACHE_IGNORE_LIBPATH);
+ RNA_def_property_ui_text(prop, "Library Path",
+ "Use this file's path for the disk cache when library linked into another file "
+ "(for local bakes per scene file, disable this option)");
+ RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
+
+ prop = RNA_def_property(srna, "point_caches", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop, "rna_Cache_list_begin", "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end", "rna_iterator_listbase_get",
+ NULL, NULL, NULL, NULL);
+ RNA_def_property_struct_type(prop, "PointCache");
+ RNA_def_property_ui_text(prop, "Point Cache List", "Point cache list");
+ rna_def_ptcache_point_caches(brna, prop);
+}
+
static void rna_def_collision(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1452,6 +1871,7 @@ static void rna_def_softbody(BlenderRNA *brna)
void RNA_def_object_force(BlenderRNA *brna)
{
+ rna_def_pointcache(brna);
rna_def_collision(brna);
rna_def_effector_weight(brna);
rna_def_field(brna);
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
new file mode 100644
index 00000000000..362baed1e7c
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -0,0 +1,3573 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Blender Foundation (2008).
+ *
+ * Adaptive time step
+ * Copyright 2011 AutoCRC
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_particle.c
+ * \ingroup RNA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "DNA_material_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_cloth_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_boid_types.h"
+#include "DNA_texture_types.h"
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "BLT_translation.h"
+
+#include "rna_internal.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#ifdef RNA_RUNTIME
+static EnumPropertyItem part_from_items[] = {
+ {PART_FROM_VERT, "VERT", 0, "Verts", ""},
+ {PART_FROM_FACE, "FACE", 0, "Faces", ""},
+ {PART_FROM_VOLUME, "VOLUME", 0, "Volume", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+#ifndef RNA_RUNTIME
+static EnumPropertyItem part_reactor_from_items[] = {
+ {PART_FROM_VERT, "VERT", 0, "Verts", ""},
+ {PART_FROM_FACE, "FACE", 0, "Faces", ""},
+ {PART_FROM_VOLUME, "VOLUME", 0, "Volume", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+static EnumPropertyItem part_dist_items[] = {
+ {PART_DISTR_JIT, "JIT", 0, "Jittered", ""},
+ {PART_DISTR_RAND, "RAND", 0, "Random", ""},
+ {PART_DISTR_GRID, "GRID", 0, "Grid", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+#ifdef RNA_RUNTIME
+static EnumPropertyItem part_hair_dist_items[] = {
+ {PART_DISTR_JIT, "JIT", 0, "Jittered", ""},
+ {PART_DISTR_RAND, "RAND", 0, "Random", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+static EnumPropertyItem part_draw_as_items[] = {
+ {PART_DRAW_NOT, "NONE", 0, "None", ""},
+ {PART_DRAW_REND, "RENDER", 0, "Rendered", ""},
+ {PART_DRAW_DOT, "DOT", 0, "Point", ""},
+ {PART_DRAW_CIRC, "CIRC", 0, "Circle", ""},
+ {PART_DRAW_CROSS, "CROSS", 0, "Cross", ""},
+ {PART_DRAW_AXIS, "AXIS", 0, "Axis", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+#ifdef RNA_RUNTIME
+static EnumPropertyItem part_hair_draw_as_items[] = {
+ {PART_DRAW_NOT, "NONE", 0, "None", ""},
+ {PART_DRAW_REND, "RENDER", 0, "Rendered", ""},
+ {PART_DRAW_PATH, "PATH", 0, "Path", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+static EnumPropertyItem part_ren_as_items[] = {
+ {PART_DRAW_NOT, "NONE", 0, "None", ""},
+ {PART_DRAW_HALO, "HALO", 0, "Halo", ""},
+ {PART_DRAW_LINE, "LINE", 0, "Line", ""},
+ {PART_DRAW_PATH, "PATH", 0, "Path", ""},
+ {PART_DRAW_OB, "OBJECT", 0, "Object", ""},
+ {PART_DRAW_GR, "GROUP", 0, "Group", ""},
+ {PART_DRAW_BB, "BILLBOARD", 0, "Billboard", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+#ifdef RNA_RUNTIME
+static EnumPropertyItem part_hair_ren_as_items[] = {
+ {PART_DRAW_NOT, "NONE", 0, "None", ""},
+ {PART_DRAW_PATH, "PATH", 0, "Path", ""},
+ {PART_DRAW_OB, "OBJECT", 0, "Object", ""},
+ {PART_DRAW_GR, "GROUP", 0, "Group", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+#ifdef RNA_RUNTIME
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_cloth.h"
+#include "BKE_colortools.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_effect.h"
+#include "BKE_material.h"
+#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_texture.h"
+
+/* use for object space hair get/set */
+static void rna_ParticleHairKey_location_object_info(PointerRNA *ptr, ParticleSystemModifierData **psmd_pt,
+ ParticleData **pa_pt)
+{
+ HairKey *hkey = (HairKey *)ptr->data;
+ Object *ob = (Object *)ptr->id.data;
+ ModifierData *md;
+ ParticleSystemModifierData *psmd = NULL;
+ ParticleSystem *psys;
+ ParticleData *pa;
+ int i;
+
+ *psmd_pt = NULL;
+ *pa_pt = NULL;
+
+ /* given the pointer HairKey *hkey, we iterate over all particles in all
+ * particle systems in the object "ob" in order to find
+ * - the ParticleSystemData to which the HairKey (and hence the particle)
+ * belongs (will be stored in psmd_pt)
+ * - the ParticleData to which the HairKey belongs (will be stored in pa_pt)
+ *
+ * not a very efficient way of getting hair key location data,
+ * but it's the best we've got at the present
+ *
+ * IDEAS: include additional information in pointerRNA beforehand,
+ * for example a pointer to the ParticleStstemModifierData to which the
+ * hairkey belongs.
+ */
+
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_ParticleSystem) {
+ psmd = (ParticleSystemModifierData *) md;
+ if (psmd && psmd->dm_final && psmd->psys) {
+ psys = psmd->psys;
+ for (i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
+ /* hairkeys are stored sequentially in memory, so we can
+ * find if it's the same particle by comparing pointers,
+ * without having to iterate over them all */
+ if ((hkey >= pa->hair) && (hkey < pa->hair + pa->totkey)) {
+ *psmd_pt = psmd;
+ *pa_pt = pa;
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void rna_ParticleHairKey_location_object_get(PointerRNA *ptr, float *values)
+{
+ HairKey *hkey = (HairKey *)ptr->data;
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystemModifierData *psmd;
+ ParticleData *pa;
+
+ rna_ParticleHairKey_location_object_info(ptr, &psmd, &pa);
+
+ if (pa) {
+ DerivedMesh *hairdm = (psmd->psys->flag & PSYS_HAIR_DYNAMICS) ? psmd->psys->hair_out_dm : NULL;
+
+ if (hairdm) {
+ MVert *mvert = CDDM_get_vert(hairdm, pa->hair_index + (hkey - pa->hair));
+ copy_v3_v3(values, mvert->co);
+ }
+ else {
+ float hairmat[4][4];
+ psys_mat_hair_to_object(ob, psmd->dm_final, psmd->psys->part->from, pa, hairmat);
+ copy_v3_v3(values, hkey->co);
+ mul_m4_v3(hairmat, values);
+ }
+ }
+ else {
+ zero_v3(values);
+ }
+}
+
+static void rna_ParticleHairKey_location_object_set(PointerRNA *ptr, const float *values)
+{
+ HairKey *hkey = (HairKey *)ptr->data;
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystemModifierData *psmd;
+ ParticleData *pa;
+
+ rna_ParticleHairKey_location_object_info(ptr, &psmd, &pa);
+
+ if (pa) {
+ DerivedMesh *hairdm = (psmd->psys->flag & PSYS_HAIR_DYNAMICS) ? psmd->psys->hair_out_dm : NULL;
+
+ if (hairdm) {
+ MVert *mvert = CDDM_get_vert(hairdm, pa->hair_index + (hkey - pa->hair));
+ copy_v3_v3(mvert->co, values);
+ }
+ else {
+ float hairmat[4][4];
+ float imat[4][4];
+
+ psys_mat_hair_to_object(ob, psmd->dm_final, psmd->psys->part->from, pa, hairmat);
+ invert_m4_m4(imat, hairmat);
+ copy_v3_v3(hkey->co, values);
+ mul_m4_v3(imat, hkey->co);
+ }
+ }
+ else {
+ zero_v3(hkey->co);
+ }
+}
+
+static void rna_ParticleHairKey_co_object(HairKey *hairkey, Object *object, ParticleSystemModifierData *modifier, ParticleData *particle,
+ float n_co[3])
+{
+
+ DerivedMesh *hairdm = (modifier->psys->flag & PSYS_HAIR_DYNAMICS) ? modifier->psys->hair_out_dm : NULL;
+ if (particle) {
+ if (hairdm) {
+ MVert *mvert = CDDM_get_vert(hairdm, particle->hair_index + (hairkey - particle->hair));
+ copy_v3_v3(n_co, mvert->co);
+ }
+ else {
+ float hairmat[4][4];
+ psys_mat_hair_to_object(object, modifier->dm_final, modifier->psys->part->from, particle, hairmat);
+ copy_v3_v3(n_co, hairkey->co);
+ mul_m4_v3(hairmat, n_co);
+ }
+ }
+ else {
+ zero_v3(n_co);
+ }
+}
+
+static void rna_Particle_uv_on_emitter(ParticleData *particle, ReportList *reports,
+ ParticleSystemModifierData *modifier, float r_uv[2])
+{
+ /*psys_particle_on_emitter(psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, nor, 0, 0, sd.orco, 0);*/
+
+ /* get uvco & mcol */
+ int num = particle->num_dmcache;
+ int from = modifier->psys->part->from;
+
+ if (!CustomData_has_layer(&modifier->dm_final->loopData, CD_MLOOPUV)) {
+ BKE_report(reports, RPT_ERROR, "Mesh has no UV data");
+ return;
+ }
+ DM_ensure_tessface(modifier->dm_final); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */
+
+ if (num == DMCACHE_NOTFOUND)
+ if (particle->num < modifier->dm_final->getNumTessFaces(modifier->dm_final))
+ num = particle->num;
+
+ /* get uvco */
+ if (r_uv && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+
+ if (num != DMCACHE_NOTFOUND) {
+ MFace *mface;
+ MTFace *mtface;
+
+ mface = modifier->dm_final->getTessFaceData(modifier->dm_final, num, CD_MFACE);
+ mtface = (MTFace *)CustomData_get_layer_n(&modifier->dm_final->faceData, CD_MTFACE, 0);
+
+ if (mface && mtface) {
+ mtface += num;
+ psys_interpolate_uvs(mtface, mface->v4, particle->fuv, r_uv);
+ return;
+ }
+ }
+ }
+
+ r_uv[0] = 0.0f;
+ r_uv[1] = 0.0f;
+}
+
+static void rna_ParticleSystem_co_hair(ParticleSystem *particlesystem, Object *object,
+ int particle_no, int step, float n_co[3])
+{
+ ParticleSettings *part = NULL;
+ ParticleData *pars = NULL;
+ ParticleCacheKey *cache = NULL;
+ int totchild = 0;
+ int path_nbr = 0;
+ int totpart;
+ int max_k = 0;
+ int step_nbr = 0;
+
+ if (particlesystem == NULL)
+ return;
+
+ part = particlesystem->part;
+ pars = particlesystem->particles;
+
+ if (particlesystem->renderdata) {
+ step_nbr = part->ren_step;
+ totchild = particlesystem->totchild;
+ }
+ else {
+ step_nbr = part->draw_step;
+ totchild = (int)((float)particlesystem->totchild * (float)(part->disp) / 100.0f);
+ }
+
+ if (part == NULL || pars == NULL || !psys_check_enabled(object, particlesystem, particlesystem->renderdata != NULL))
+ return;
+
+ if (part->ren_as == PART_DRAW_OB || part->ren_as == PART_DRAW_GR || part->ren_as == PART_DRAW_NOT)
+ return;
+
+ /* can happen for disconnected/global hair */
+ if (part->type == PART_HAIR && !particlesystem->childcache)
+ totchild = 0;
+
+ totpart = particlesystem->totpart;
+
+ if (particle_no >= totpart + totchild)
+ return;
+
+ if (part->ren_as == PART_DRAW_PATH && particlesystem->pathcache)
+ path_nbr = 1 << step_nbr;
+ if (part->kink == PART_KINK_SPIRAL)
+ path_nbr += part->kink_extra_steps;
+
+ if (particle_no < totpart) {
+
+ if (path_nbr) {
+ cache = particlesystem->pathcache[particle_no];
+ max_k = (int)cache->segments;
+ }
+
+ }
+ else {
+
+ if (path_nbr) {
+ cache = particlesystem->childcache[particle_no - totpart];
+
+ if (cache->segments < 0)
+ max_k = 0;
+ else
+ max_k = (int)cache->segments;
+ }
+ }
+
+ /*strands key loop data stored in cache + step->co*/
+ if (path_nbr) {
+ if (step >= 0 && step <= path_nbr) {
+ if (step <= max_k) {
+ copy_v3_v3(n_co, (cache + step)->co);
+ mul_m4_v3(particlesystem->imat, n_co);
+ mul_m4_v3(object->obmat, n_co);
+ }
+ }
+ }
+
+}
+
+
+static EnumPropertyItem *rna_Particle_Material_itemf(bContext *C, PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ Object *ob = CTX_data_pointer_get(C, "object").data;
+ Material *ma;
+ EnumPropertyItem *item = NULL;
+ EnumPropertyItem tmp = {0, "", 0, "", ""};
+ int totitem = 0;
+ int i;
+
+ if (ob && ob->totcol > 0) {
+ for (i = 1; i <= ob->totcol; i++) {
+ ma = give_current_material(ob, i);
+ tmp.value = i;
+ tmp.icon = ICON_MATERIAL_DATA;
+ if (ma) {
+ tmp.name = ma->id.name + 2;
+ tmp.identifier = tmp.name;
+ }
+ else {
+ tmp.name = "Default Material";
+ tmp.identifier = tmp.name;
+ }
+ RNA_enum_item_add(&item, &totitem, &tmp);
+ }
+ }
+ else {
+ tmp.value = 1;
+ tmp.icon = ICON_MATERIAL_DATA;
+ tmp.name = "Default Material";
+ tmp.identifier = tmp.name;
+ RNA_enum_item_add(&item, &totitem, &tmp);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+
+/* return < 0 means invalid (no matching tessellated face could be found). */
+static int rna_ParticleSystem_tessfaceidx_on_emitter(ParticleSystem *particlesystem,
+ ParticleSystemModifierData *modifier, ParticleData *particle,
+ int particle_no, float (**r_fuv)[4])
+{
+ ParticleSettings *part = NULL;
+ int totpart;
+ int totchild = 0;
+ int totface;
+ int num = -1;
+
+ DM_ensure_tessface(modifier->dm_final); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */
+ totface = modifier->dm_final->getNumTessFaces(modifier->dm_final);
+
+ /* 1. check that everything is ok & updated */
+ if (!particlesystem || !totface) {
+ return num;
+ }
+
+ part = particlesystem->part;
+
+ if (particlesystem->renderdata) {
+ totchild = particlesystem->totchild;
+ }
+ else {
+ totchild = (int)((float)particlesystem->totchild * (float)(part->disp) / 100.0f);
+ }
+
+ /* can happen for disconnected/global hair */
+ if (part->type == PART_HAIR && !particlesystem->childcache)
+ totchild = 0;
+
+ totpart = particlesystem->totpart;
+
+ if (particle_no >= totpart + totchild)
+ return num;
+
+ /* 2. get matching face index. */
+ if (particle_no < totpart) {
+ num = (ELEM(particle->num_dmcache, DMCACHE_ISCHILD, DMCACHE_NOTFOUND)) ? particle->num : particle->num_dmcache;
+
+ if (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ if (num != DMCACHE_NOTFOUND && num < totface) {
+ *r_fuv = &particle->fuv;
+ return num;
+ }
+ }
+ }
+ else {
+ ChildParticle *cpa = particlesystem->child + particle_no - totpart;
+ num = cpa->num;
+
+ if (part->childtype == PART_CHILD_FACES) {
+ if (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ if (num != DMCACHE_NOTFOUND && num < totface) {
+ *r_fuv = &cpa->fuv;
+ return num;
+ }
+ }
+ }
+ else {
+ ParticleData *parent = particlesystem->particles + cpa->parent;
+ num = parent->num_dmcache;
+
+ if (num == DMCACHE_NOTFOUND)
+ num = parent->num;
+
+ if (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ if (num != DMCACHE_NOTFOUND && num < totface) {
+ *r_fuv = &parent->fuv;
+ return num;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static void rna_ParticleSystem_uv_on_emitter(ParticleSystem *particlesystem, ReportList *reports,
+ ParticleSystemModifierData *modifier, ParticleData *particle,
+ int particle_no, int uv_no, float r_uv[2])
+{
+ if (!CustomData_has_layer(&modifier->dm_final->loopData, CD_MLOOPUV)) {
+ BKE_report(reports, RPT_ERROR, "Mesh has no UV data");
+ zero_v2(r_uv);
+ return;
+ }
+
+ {
+ float (*fuv)[4];
+ /* Note all sanity checks are done in this helper func. */
+ const int num = rna_ParticleSystem_tessfaceidx_on_emitter(particlesystem, modifier, particle,
+ particle_no, &fuv);
+
+ if (num < 0) {
+ /* No matching face found. */
+ zero_v2(r_uv);
+ }
+ else {
+ MFace *mface = modifier->dm_final->getTessFaceData(modifier->dm_final, num, CD_MFACE);
+ MTFace *mtface = (MTFace *)CustomData_get_layer_n(&modifier->dm_final->faceData, CD_MTFACE, uv_no);
+
+ psys_interpolate_uvs(&mtface[num], mface->v4, *fuv, r_uv);
+ }
+ }
+}
+
+static void rna_ParticleSystem_mcol_on_emitter(ParticleSystem *particlesystem, ReportList *reports,
+ ParticleSystemModifierData *modifier, ParticleData *particle,
+ int particle_no, int vcol_no, float r_mcol[3])
+{
+ if (!CustomData_has_layer(&modifier->dm_final->loopData, CD_MLOOPCOL)) {
+ BKE_report(reports, RPT_ERROR, "Mesh has no VCol data");
+ zero_v3(r_mcol);
+ return;
+ }
+
+ {
+ float (*fuv)[4];
+ /* Note all sanity checks are done in this helper func. */
+ const int num = rna_ParticleSystem_tessfaceidx_on_emitter(particlesystem, modifier, particle,
+ particle_no, &fuv);
+
+ if (num < 0) {
+ /* No matching face found. */
+ zero_v3(r_mcol);
+ }
+ else {
+ MFace *mface = modifier->dm_final->getTessFaceData(modifier->dm_final, num, CD_MFACE);
+ MCol *mc = (MCol *)CustomData_get_layer_n(&modifier->dm_final->faceData, CD_MCOL, vcol_no);
+ MCol mcol;
+
+ psys_interpolate_mcol(&mc[num * 4], mface->v4, *fuv, &mcol);
+ r_mcol[0] = (float)mcol.b / 255.0f;
+ r_mcol[1] = (float)mcol.g / 255.0f;
+ r_mcol[2] = (float)mcol.r / 255.0f;
+ }
+ }
+}
+
+static void rna_ParticleSystem_set_resolution(ParticleSystem *particlesystem, Scene *scene, Object *object, int resolution)
+{
+ if (resolution == eModifierMode_Render) {
+ ParticleSystemModifierData *psmd = psys_get_modifier(object, particlesystem);
+ float mat[4][4];
+
+ unit_m4(mat);
+
+ psys_render_set(object, particlesystem, mat, mat, 1, 1, 0.f);
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ particle_system_update(scene, object, particlesystem, true);
+ }
+ else {
+ ParticleSystemModifierData *psmd = psys_get_modifier(object, particlesystem);
+
+ if (particlesystem->renderdata) {
+ psys_render_restore(object, particlesystem);
+ }
+
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ particle_system_update(scene, object, particlesystem, false);
+ }
+}
+
+static void particle_recalc(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr, short flag)
+{
+ if (ptr->type == &RNA_ParticleSystem) {
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ psys->recalc = flag;
+
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
+ }
+ else
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA | flag);
+
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+}
+static void rna_Particle_redo(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ particle_recalc(bmain, scene, ptr, PSYS_RECALC_REDO);
+}
+
+static void rna_Particle_redo_dependency(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ DAG_relations_tag_update(bmain);
+ rna_Particle_redo(bmain, scene, ptr);
+}
+
+static void rna_Particle_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET);
+}
+
+static void rna_Particle_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ DAG_relations_tag_update(bmain);
+ rna_Particle_reset(bmain, scene, ptr);
+}
+
+static void rna_Particle_change_type(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET | PSYS_RECALC_TYPE);
+}
+
+static void rna_Particle_change_physics(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET | PSYS_RECALC_PHYS);
+}
+
+static void rna_Particle_redo_child(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ particle_recalc(bmain, scene, ptr, PSYS_RECALC_CHILD);
+}
+
+static void rna_Particle_cloth_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
+}
+
+
+static ParticleSystem *rna_particle_system_for_target(Object *ob, ParticleTarget *target)
+{
+ ParticleSystem *psys;
+ ParticleTarget *pt;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ for (pt = psys->targets.first; pt; pt = pt->next)
+ if (pt == target)
+ return psys;
+
+ return NULL;
+}
+
+static void rna_Particle_target_reset(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ if (ptr->type == &RNA_ParticleTarget) {
+ Object *ob = (Object *)ptr->id.data;
+ ParticleTarget *pt = (ParticleTarget *)ptr->data;
+ ParticleSystem *kpsys = NULL, *psys = rna_particle_system_for_target(ob, pt);
+
+ if (pt->ob == ob || pt->ob == NULL) {
+ kpsys = BLI_findlink(&ob->particlesystem, pt->psys - 1);
+
+ if (kpsys)
+ pt->flag |= PTARGET_VALID;
+ else
+ pt->flag &= ~PTARGET_VALID;
+ }
+ else {
+ if (pt->ob)
+ kpsys = BLI_findlink(&pt->ob->particlesystem, pt->psys - 1);
+
+ if (kpsys)
+ pt->flag |= PTARGET_VALID;
+ else
+ pt->flag &= ~PTARGET_VALID;
+ }
+
+ psys->recalc = PSYS_RECALC_RESET;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ DAG_relations_tag_update(bmain);
+ }
+
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+}
+
+static void rna_Particle_target_redo(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ if (ptr->type == &RNA_ParticleTarget) {
+ Object *ob = (Object *)ptr->id.data;
+ ParticleTarget *pt = (ParticleTarget *)ptr->data;
+ ParticleSystem *psys = rna_particle_system_for_target(ob, pt);
+
+ psys->recalc = PSYS_RECALC_REDO;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+ }
+}
+
+static void rna_Particle_hair_dynamics(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ if (psys && !psys->clmd) {
+ psys->clmd = (ClothModifierData *)modifier_new(eModifierType_Cloth);
+ psys->clmd->sim_parms->goalspring = 0.0f;
+ psys->clmd->sim_parms->flags |= CLOTH_SIMSETTINGS_FLAG_GOAL | CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
+ psys->clmd->coll_parms->flags &= ~CLOTH_COLLSETTINGS_FLAG_SELF;
+ rna_Particle_redo(bmain, scene, ptr);
+ }
+ else
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+}
+static PointerRNA rna_particle_settings_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ ParticleSettings *part = psys->part;
+
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleSettings, part);
+}
+
+static void rna_particle_settings_set(PointerRNA *ptr, PointerRNA value)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ int old_type = 0;
+
+
+ if (psys->part) {
+ old_type = psys->part->type;
+ id_us_min(&psys->part->id);
+ }
+
+ psys->part = (ParticleSettings *)value.data;
+
+ if (psys->part) {
+ id_us_plus(&psys->part->id);
+ psys_check_boid_data(psys);
+ if (old_type != psys->part->type)
+ psys->recalc |= PSYS_RECALC_TYPE;
+ }
+}
+static void rna_Particle_abspathtime_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ float delta = settings->end + settings->lifetime - settings->sta;
+ if (settings->draw & PART_ABS_PATH_TIME) {
+ settings->path_start = settings->sta + settings->path_start * delta;
+ settings->path_end = settings->sta + settings->path_end * delta;
+ }
+ else {
+ settings->path_start = (settings->path_start - settings->sta) / delta;
+ settings->path_end = (settings->path_end - settings->sta) / delta;
+ }
+ rna_Particle_redo(bmain, scene, ptr);
+}
+static void rna_PartSettings_start_set(struct PointerRNA *ptr, float value)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+
+ /* check for clipping */
+ if (value > settings->end)
+ value = settings->end;
+
+ /*if (settings->type==PART_REACTOR && value < 1.0) */
+ /* value = 1.0; */
+ /*else */
+ if (value < MINAFRAMEF)
+ value = MINAFRAMEF;
+
+ settings->sta = value;
+}
+
+static void rna_PartSettings_end_set(struct PointerRNA *ptr, float value)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+
+ /* check for clipping */
+ if (value < settings->sta)
+ value = settings->sta;
+
+ settings->end = value;
+}
+
+static void rna_PartSetings_timestep_set(struct PointerRNA *ptr, float value)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+
+ settings->timetweak = value / 0.04f;
+}
+
+static float rna_PartSettings_timestep_get(struct PointerRNA *ptr)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+
+ return settings->timetweak * 0.04f;
+}
+
+static void rna_PartSetting_hairlength_set(struct PointerRNA *ptr, float value)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ settings->normfac = value / 4.f;
+}
+
+static float rna_PartSetting_hairlength_get(struct PointerRNA *ptr)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ return settings->normfac * 4.f;
+}
+
+static void rna_PartSetting_linelentail_set(struct PointerRNA *ptr, float value)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ settings->draw_line[0] = value;
+}
+
+static float rna_PartSetting_linelentail_get(struct PointerRNA *ptr)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ return settings->draw_line[0];
+}
+static void rna_PartSetting_pathstartend_range(PointerRNA *ptr, float *min, float *max,
+ float *UNUSED(softmin), float *UNUSED(softmax))
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+
+ if (settings->type == PART_HAIR) {
+ *min = 0.0f;
+ *max = (settings->draw & PART_ABS_PATH_TIME) ? 100.0f : 1.0f;
+ }
+ else {
+ *min = (settings->draw & PART_ABS_PATH_TIME) ? settings->sta : 0.0f;
+ *max = (settings->draw & PART_ABS_PATH_TIME) ? MAXFRAMEF : 1.0f;
+ }
+}
+static void rna_PartSetting_linelenhead_set(struct PointerRNA *ptr, float value)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ settings->draw_line[1] = value;
+}
+
+static float rna_PartSetting_linelenhead_get(struct PointerRNA *ptr)
+{
+ ParticleSettings *settings = (ParticleSettings *)ptr->data;
+ return settings->draw_line[1];
+}
+
+
+static int rna_PartSettings_is_fluid_get(PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->data;
+
+ return part->type == PART_FLUID;
+}
+
+static void rna_ParticleSettings_use_clump_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ ParticleSettings *part = ptr->data;
+
+ if (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) {
+ if (!part->clumpcurve) {
+ BKE_particlesettings_clump_curve_init(part);
+ }
+ }
+
+ rna_Particle_redo_child(bmain, scene, ptr);
+}
+
+static void rna_ParticleSettings_use_roughness_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ ParticleSettings *part = ptr->data;
+
+ if (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) {
+ if (!part->roughcurve) {
+ BKE_particlesettings_rough_curve_init(part);
+ }
+ }
+
+ rna_Particle_redo_child(bmain, scene, ptr);
+}
+
+static void rna_ParticleSystem_name_set(PointerRNA *ptr, const char *value)
+{
+ Object *ob = ptr->id.data;
+ ParticleSystem *part = (ParticleSystem *)ptr->data;
+
+ /* copy the new name into the name slot */
+ BLI_strncpy_utf8(part->name, value, sizeof(part->name));
+
+ BLI_uniquename(&ob->particlesystem, part, DATA_("ParticleSystem"), '.', offsetof(ParticleSystem, name),
+ sizeof(part->name));
+}
+
+static PointerRNA rna_ParticleSystem_active_particle_target_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ ParticleTarget *pt = psys->targets.first;
+
+ for (; pt; pt = pt->next) {
+ if (pt->flag & PTARGET_CURRENT)
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleTarget, pt);
+ }
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleTarget, NULL);
+}
+static void rna_ParticleSystem_active_particle_target_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&psys->targets) - 1);
+}
+
+static int rna_ParticleSystem_active_particle_target_index_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ ParticleTarget *pt = psys->targets.first;
+ int i = 0;
+
+ for (; pt; pt = pt->next, i++)
+ if (pt->flag & PTARGET_CURRENT)
+ return i;
+
+ return 0;
+}
+
+static void rna_ParticleSystem_active_particle_target_index_set(struct PointerRNA *ptr, int value)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ ParticleTarget *pt = psys->targets.first;
+ int i = 0;
+
+ for (; pt; pt = pt->next, i++) {
+ if (i == value)
+ pt->flag |= PTARGET_CURRENT;
+ else
+ pt->flag &= ~PTARGET_CURRENT;
+ }
+}
+
+static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *str)
+{
+ ParticleTarget *pt = ptr->data;
+
+ if (pt->flag & PTARGET_VALID) {
+ ParticleSystem *psys = NULL;
+
+ if (pt->ob)
+ psys = BLI_findlink(&pt->ob->particlesystem, pt->psys - 1);
+ else {
+ Object *ob = (Object *) ptr->id.data;
+ psys = BLI_findlink(&ob->particlesystem, pt->psys - 1);
+ }
+
+ if (psys) {
+ if (pt->ob)
+ sprintf(str, "%s: %s", pt->ob->id.name + 2, psys->name);
+ else
+ strcpy(str, psys->name);
+ }
+ else
+ strcpy(str, "Invalid target!");
+ }
+ else
+ strcpy(str, "Invalid target!");
+}
+
+static int rna_ParticleTarget_name_length(PointerRNA *ptr)
+{
+ char tstr[MAX_ID_NAME + MAX_ID_NAME + 64];
+
+ rna_ParticleTarget_name_get(ptr, tstr);
+
+ return strlen(tstr);
+}
+
+static int particle_id_check(PointerRNA *ptr)
+{
+ ID *id = ptr->id.data;
+
+ return (GS(id->name) == ID_PA);
+}
+
+static char *rna_SPHFluidSettings_path(PointerRNA *ptr)
+{
+ SPHFluidSettings *fluid = (SPHFluidSettings *)ptr->data;
+
+ if (particle_id_check(ptr)) {
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ if (part->fluid == fluid)
+ return BLI_sprintfN("fluid");
+ }
+ return NULL;
+}
+
+static int rna_ParticleSystem_multiple_caches_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ return (psys->ptcaches.first != psys->ptcaches.last);
+}
+static int rna_ParticleSystem_editable_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ return psys_check_edited(psys);
+}
+static int rna_ParticleSystem_edited_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ if (psys->part && psys->part->type == PART_HAIR)
+ return (psys->flag & PSYS_EDITED || (psys->edit && psys->edit->edited));
+ else
+ return (psys->pointcache->edit && psys->pointcache->edit->edited);
+}
+static PointerRNA rna_ParticleDupliWeight_active_get(PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+ ParticleDupliWeight *dw = part->dupliweights.first;
+
+ for (; dw; dw = dw->next) {
+ if (dw->flag & PART_DUPLIW_CURRENT)
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleDupliWeight, dw);
+ }
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleTarget, NULL);
+}
+static void rna_ParticleDupliWeight_active_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&part->dupliweights) - 1);
+}
+
+static int rna_ParticleDupliWeight_active_index_get(PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+ ParticleDupliWeight *dw = part->dupliweights.first;
+ int i = 0;
+
+ for (; dw; dw = dw->next, i++)
+ if (dw->flag & PART_DUPLIW_CURRENT)
+ return i;
+
+ return 0;
+}
+
+static void rna_ParticleDupliWeight_active_index_set(struct PointerRNA *ptr, int value)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+ ParticleDupliWeight *dw = part->dupliweights.first;
+ int i = 0;
+
+ for (; dw; dw = dw->next, i++) {
+ if (i == value)
+ dw->flag |= PART_DUPLIW_CURRENT;
+ else
+ dw->flag &= ~PART_DUPLIW_CURRENT;
+ }
+}
+
+static void rna_ParticleDupliWeight_name_get(PointerRNA *ptr, char *str)
+{
+ ParticleDupliWeight *dw = ptr->data;
+
+ if (dw->ob)
+ sprintf(str, "%s: %i", dw->ob->id.name + 2, dw->count);
+ else
+ strcpy(str, "No object");
+}
+
+static int rna_ParticleDupliWeight_name_length(PointerRNA *ptr)
+{
+ char tstr[MAX_ID_NAME + 64];
+
+ rna_ParticleDupliWeight_name_get(ptr, tstr);
+
+ return strlen(tstr);
+}
+
+static EnumPropertyItem *rna_Particle_from_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ /*if (part->type==PART_REACTOR) */
+ /* return part_reactor_from_items; */
+ /*else */
+ return part_from_items;
+}
+
+static EnumPropertyItem *rna_Particle_dist_itemf(bContext *UNUSED(C), PointerRNA *ptr,
+ PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ ParticleSettings *part = ptr->id.data;
+
+ if (part->type == PART_HAIR)
+ return part_hair_dist_items;
+ else
+ return part_dist_items;
+}
+
+static EnumPropertyItem *rna_Particle_draw_as_itemf(bContext *UNUSED(C), PointerRNA *ptr,
+ PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ ParticleSettings *part = ptr->id.data;
+
+ if (part->type == PART_HAIR)
+ return part_hair_draw_as_items;
+ else
+ return part_draw_as_items;
+}
+
+static EnumPropertyItem *rna_Particle_ren_as_itemf(bContext *UNUSED(C), PointerRNA *ptr,
+ PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ ParticleSettings *part = ptr->id.data;
+
+ if (part->type == PART_HAIR)
+ return part_hair_ren_as_items;
+ else
+ return part_ren_as_items;
+}
+
+static PointerRNA rna_Particle_field1_get(PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ /* weak */
+ if (!part->pd)
+ part->pd = object_add_collision_fields(0);
+
+ return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd);
+}
+
+static PointerRNA rna_Particle_field2_get(PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->id.data;
+
+ /* weak */
+ if (!part->pd2)
+ part->pd2 = object_add_collision_fields(0);
+
+ return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd2);
+}
+
+static void psys_vg_name_get__internal(PointerRNA *ptr, char *value, int index)
+{
+ Object *ob = ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ if (psys->vgroup[index] > 0) {
+ bDeformGroup *defGroup = BLI_findlink(&ob->defbase, psys->vgroup[index] - 1);
+
+ if (defGroup) {
+ strcpy(value, defGroup->name);
+ return;
+ }
+ }
+
+ value[0] = '\0';
+}
+static int psys_vg_name_len__internal(PointerRNA *ptr, int index)
+{
+ Object *ob = ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ if (psys->vgroup[index] > 0) {
+ bDeformGroup *defGroup = BLI_findlink(&ob->defbase, psys->vgroup[index] - 1);
+
+ if (defGroup) {
+ return strlen(defGroup->name);
+ }
+ }
+ return 0;
+}
+static void psys_vg_name_set__internal(PointerRNA *ptr, const char *value, int index)
+{
+ Object *ob = ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+
+ if (value[0] == '\0') {
+ psys->vgroup[index] = 0;
+ }
+ else {
+ int defgrp_index = defgroup_name_index(ob, value);
+
+ if (defgrp_index == -1)
+ return;
+
+ psys->vgroup[index] = defgrp_index + 1;
+ }
+}
+
+static char *rna_ParticleSystem_path(PointerRNA *ptr)
+{
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ char name_esc[sizeof(psys->name) * 2];
+
+ BLI_strescape(name_esc, psys->name, sizeof(name_esc));
+ return BLI_sprintfN("particle_systems[\"%s\"]", name_esc);
+}
+
+static void rna_ParticleSettings_mtex_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->data;
+ rna_iterator_array_begin(iter, (void *)part->mtex, sizeof(MTex *), MAX_MTEX, 0, NULL);
+}
+
+static PointerRNA rna_ParticleSettings_active_texture_get(PointerRNA *ptr)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->data;
+ Tex *tex;
+
+ tex = give_current_particle_texture(part);
+ return rna_pointer_inherit_refine(ptr, &RNA_Texture, tex);
+}
+
+static void rna_ParticleSettings_active_texture_set(PointerRNA *ptr, PointerRNA value)
+{
+ ParticleSettings *part = (ParticleSettings *)ptr->data;
+
+ set_current_particle_texture(part, value.data);
+}
+
+/* irritating string functions for each index :/ */
+static void rna_ParticleVGroup_name_get_0(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 0); }
+static void rna_ParticleVGroup_name_get_1(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 1); }
+static void rna_ParticleVGroup_name_get_2(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 2); }
+static void rna_ParticleVGroup_name_get_3(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 3); }
+static void rna_ParticleVGroup_name_get_4(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 4); }
+static void rna_ParticleVGroup_name_get_5(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 5); }
+static void rna_ParticleVGroup_name_get_6(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 6); }
+static void rna_ParticleVGroup_name_get_7(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 7); }
+static void rna_ParticleVGroup_name_get_8(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 8); }
+static void rna_ParticleVGroup_name_get_9(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 9); }
+static void rna_ParticleVGroup_name_get_10(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 10); }
+static void rna_ParticleVGroup_name_get_11(PointerRNA *ptr, char *value) { psys_vg_name_get__internal(ptr, value, 11); }
+
+static int rna_ParticleVGroup_name_len_0(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 0); }
+static int rna_ParticleVGroup_name_len_1(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 1); }
+static int rna_ParticleVGroup_name_len_2(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 2); }
+static int rna_ParticleVGroup_name_len_3(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 3); }
+static int rna_ParticleVGroup_name_len_4(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 4); }
+static int rna_ParticleVGroup_name_len_5(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 5); }
+static int rna_ParticleVGroup_name_len_6(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 6); }
+static int rna_ParticleVGroup_name_len_7(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 7); }
+static int rna_ParticleVGroup_name_len_8(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 8); }
+static int rna_ParticleVGroup_name_len_9(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 9); }
+static int rna_ParticleVGroup_name_len_10(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 10); }
+static int rna_ParticleVGroup_name_len_11(PointerRNA *ptr) { return psys_vg_name_len__internal(ptr, 11); }
+
+static void rna_ParticleVGroup_name_set_0(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 0); }
+static void rna_ParticleVGroup_name_set_1(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 1); }
+static void rna_ParticleVGroup_name_set_2(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 2); }
+static void rna_ParticleVGroup_name_set_3(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 3); }
+static void rna_ParticleVGroup_name_set_4(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 4); }
+static void rna_ParticleVGroup_name_set_5(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 5); }
+static void rna_ParticleVGroup_name_set_6(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 6); }
+static void rna_ParticleVGroup_name_set_7(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 7); }
+static void rna_ParticleVGroup_name_set_8(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 8); }
+static void rna_ParticleVGroup_name_set_9(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 9); }
+static void rna_ParticleVGroup_name_set_10(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 10); }
+static void rna_ParticleVGroup_name_set_11(PointerRNA *ptr, const char *value) { psys_vg_name_set__internal(ptr, value, 11); }
+
+
+#else
+
+static void rna_def_particle_hair_key(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ srna = RNA_def_struct(brna, "ParticleHairKey", NULL);
+ RNA_def_struct_sdna(srna, "HairKey");
+ RNA_def_struct_ui_text(srna, "Particle Hair Key", "Particle key for hair particle system");
+
+ prop = RNA_def_property(srna, "time", PROP_FLOAT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Time", "Relative time of key over hair length");
+
+ prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_UNSIGNED);
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Weight", "Weight for cloth simulation");
+
+ prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Location (Object Space)", "Location of the hair key in object space");
+ RNA_def_property_float_funcs(prop, "rna_ParticleHairKey_location_object_get",
+ "rna_ParticleHairKey_location_object_set", NULL);
+
+ prop = RNA_def_property(srna, "co_local", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "co");
+ RNA_def_property_ui_text(prop, "Location",
+ "Location of the hair key in its local coordinate system, "
+ "relative to the emitting face");
+
+ /* Aided co func */
+ func = RNA_def_function(srna, "co_object", "rna_ParticleHairKey_co_object");
+ RNA_def_function_ui_description(func, "Obtain hairkey location with particle and modifier data");
+ parm = RNA_def_pointer(func, "object", "Object", "", "Object");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "modifier", "ParticleSystemModifier", "", "Particle modifier");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "particle", "Particle", "", "hair particle");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_float_vector(func, "co", 3, NULL, -FLT_MAX, FLT_MAX, "Co",
+ "Exported hairkey location", -1e4, 1e4);
+ RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_function_output(func, parm);
+}
+
+static void rna_def_particle_key(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ParticleKey", NULL);
+ RNA_def_struct_ui_text(srna, "Particle Key", "Key location for a particle over time");
+
+ prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "co");
+ RNA_def_property_ui_text(prop, "Location", "Key location");
+
+ prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "vel");
+ RNA_def_property_ui_text(prop, "Velocity", "Key velocity");
+
+ prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_float_sdna(prop, NULL, "rot");
+ RNA_def_property_ui_text(prop, "Rotation", "Key rotation quaternion");
+
+ prop = RNA_def_property(srna, "angular_velocity", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "ave");
+ RNA_def_property_ui_text(prop, "Angular Velocity", "Key angular velocity");
+
+ prop = RNA_def_property(srna, "time", PROP_FLOAT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Time", "Time of key over the simulation");
+}
+
+static void rna_def_child_particle(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ /*PropertyRNA *prop; */
+
+ srna = RNA_def_struct(brna, "ChildParticle", NULL);
+ RNA_def_struct_ui_text(srna, "Child Particle",
+ "Child particle interpolated from simulated or edited particles");
+
+/* int num, parent; *//* num is face index on the final derived mesh */
+
+/* int pa[4]; *//* nearest particles to the child, used for the interpolation */
+/* float w[4]; *//* interpolation weights for the above particles */
+/* float fuv[4], foffset; *//* face vertex weights and offset */
+/* float rand[3]; */
+}
+
+static void rna_def_particle(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ static EnumPropertyItem alive_items[] = {
+ /*{PARS_KILLED, "KILLED", 0, "Killed", ""}, */
+ {PARS_DEAD, "DEAD", 0, "Dead", ""},
+ {PARS_UNBORN, "UNBORN", 0, "Unborn", ""},
+ {PARS_ALIVE, "ALIVE", 0, "Alive", ""},
+ {PARS_DYING, "DYING", 0, "Dying", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "Particle", NULL);
+ RNA_def_struct_sdna(srna, "ParticleData");
+ RNA_def_struct_ui_text(srna, "Particle", "Particle in a particle system");
+
+ /* Particle State & Previous State */
+ prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "state.co");
+ RNA_def_property_ui_text(prop, "Particle Location", "");
+
+ prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "state.vel");
+ RNA_def_property_ui_text(prop, "Particle Velocity", "");
+
+ prop = RNA_def_property(srna, "angular_velocity", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "state.ave");
+ RNA_def_property_ui_text(prop, "Angular Velocity", "");
+
+ prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_float_sdna(prop, NULL, "state.rot");
+ RNA_def_property_ui_text(prop, "Rotation", "");
+
+ prop = RNA_def_property(srna, "prev_location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "prev_state.co");
+ RNA_def_property_ui_text(prop, "Previous Particle Location", "");
+
+ prop = RNA_def_property(srna, "prev_velocity", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "prev_state.vel");
+ RNA_def_property_ui_text(prop, "Previous Particle Velocity", "");
+
+ prop = RNA_def_property(srna, "prev_angular_velocity", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "prev_state.ave");
+ RNA_def_property_ui_text(prop, "Previous Angular Velocity", "");
+
+ prop = RNA_def_property(srna, "prev_rotation", PROP_FLOAT, PROP_QUATERNION);
+ RNA_def_property_float_sdna(prop, NULL, "prev_state.rot");
+ RNA_def_property_ui_text(prop, "Previous Rotation", "");
+
+ /* Hair & Keyed Keys */
+
+ prop = RNA_def_property(srna, "hair_keys", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "hair", "totkey");
+ RNA_def_property_struct_type(prop, "ParticleHairKey");
+ RNA_def_property_ui_text(prop, "Hair", "");
+
+ prop = RNA_def_property(srna, "particle_keys", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "keys", "totkey");
+ RNA_def_property_struct_type(prop, "ParticleKey");
+ RNA_def_property_ui_text(prop, "Keyed States", "");
+/* */
+/* float fuv[4], foffset; *//* coordinates on face/edge number "num" and depth along*/
+/* *//* face normal for volume emission */
+
+ prop = RNA_def_property(srna, "birth_time", PROP_FLOAT, PROP_TIME);
+ RNA_def_property_float_sdna(prop, NULL, "time");
+/* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ RNA_def_property_ui_text(prop, "Birth Time", "");
+
+ prop = RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_TIME);
+/* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ RNA_def_property_ui_text(prop, "Lifetime", "");
+
+ prop = RNA_def_property(srna, "die_time", PROP_FLOAT, PROP_TIME);
+ RNA_def_property_float_sdna(prop, NULL, "dietime");
+/* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ RNA_def_property_ui_text(prop, "Die Time", "");
+
+ prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE);
+/* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ RNA_def_property_ui_text(prop, "Size", "");
+
+/* */
+/* int num; *//* index to vert/edge/face */
+/* int num_dmcache; *//* index to derived mesh data (face) to avoid slow lookups */
+/* int pad; */
+/* */
+/* int totkey; */
+
+ /* flag */
+ prop = RNA_def_property(srna, "is_exist", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", PARS_UNEXIST);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Exists", "");
+
+ prop = RNA_def_property(srna, "is_visible", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", PARS_NO_DISP);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Visible", "");
+
+ prop = RNA_def_property(srna, "alive_state", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "alive");
+ RNA_def_property_enum_items(prop, alive_items);
+ RNA_def_property_ui_text(prop, "Alive State", "");
+
+/* short rt2; */
+
+/* UVs */
+ func = RNA_def_function(srna, "uv_on_emitter", "rna_Particle_uv_on_emitter");
+ RNA_def_function_ui_description(func, "Obtain uv for particle on derived mesh");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "modifier", "ParticleSystemModifier", "", "Particle modifier");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_property(func, "uv", PROP_FLOAT, PROP_COORDS);
+ RNA_def_property_array(parm, 2);
+ RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_function_output(func, parm);
+}
+
+static void rna_def_particle_dupliweight(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ParticleDupliWeight", NULL);
+ RNA_def_struct_ui_text(srna, "Particle Dupliobject Weight", "Weight of a particle dupliobject in a group");
+ RNA_def_struct_sdna(srna, "ParticleDupliWeight");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleDupliWeight_name_get",
+ "rna_ParticleDupliWeight_name_length", NULL);
+ RNA_def_property_ui_text(prop, "Name", "Particle dupliobject name");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "count", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_range(prop, 0, SHRT_MAX);
+ RNA_def_property_ui_text(prop, "Count",
+ "The number of times this object is repeated with respect to other objects");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+}
+
+static void rna_def_fluid_settings(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem sph_solver_items[] = {
+ {SPH_SOLVER_DDR, "DDR", 0, "Double-Density", "An artistic solver with strong surface tension effects (original)"},
+ {SPH_SOLVER_CLASSICAL, "CLASSICAL", 0, "Classical", "A more physically-accurate solver"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "SPHFluidSettings", NULL);
+ RNA_def_struct_path_func(srna, "rna_SPHFluidSettings_path");
+ RNA_def_struct_ui_text(srna, "SPH Fluid Settings", "Settings for particle fluids physics");
+
+ /* Fluid settings */
+ prop = RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "solver");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, sph_solver_items);
+ RNA_def_property_ui_text(prop, "SPH Solver", "The code used to calculate internal forces on particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "spring_force", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "spring_k");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Spring Force", "Spring force");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "fluid_radius", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "radius");
+ RNA_def_property_range(prop, 0.0f, 20.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Interaction Radius", "Fluid interaction radius");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "rest_length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 2.0f);
+ RNA_def_property_ui_text(prop, "Rest Length", "Spring rest length (factor of particle radius)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_viscoelastic_springs", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_VISCOELASTIC_SPRINGS);
+ RNA_def_property_ui_text(prop, "Viscoelastic Springs", "Use viscoelastic springs instead of Hooke's springs");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_initial_rest_length", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_CURRENT_REST_LENGTH);
+ RNA_def_property_ui_text(prop, "Initial Rest Length",
+ "Use the initial length as spring rest length instead of 2 * particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "plasticity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "plasticity_constant");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Plasticity",
+ "How much the spring rest length can change after the elastic limit is crossed");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "yield_ratio", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "yield_ratio");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Elastic Limit",
+ "How much the spring has to be stretched/compressed in order to change it's rest length");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "spring_frames", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_text(prop, "Spring Frames",
+ "Create springs for this number of frames since particles birth (0 is always)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* Viscosity */
+ prop = RNA_def_property(srna, "linear_viscosity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "viscosity_omega");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Viscosity", "Linear viscosity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "stiff_viscosity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "viscosity_beta");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Stiff viscosity", "Creates viscosity for expanding fluid");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* Double density relaxation */
+ prop = RNA_def_property(srna, "stiffness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "stiffness_k");
+ RNA_def_property_range(prop, 0.0f, 1000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Stiffness", "How incompressible the fluid is (speed of sound)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "repulsion", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "stiffness_knear");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Repulsion Factor",
+ "How strongly the fluid tries to keep from clustering (factor of stiffness)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "rest_density", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rest_density");
+ RNA_def_property_range(prop, 0.0f, 10000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Rest Density", "Fluid rest density");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* Buoyancy */
+ prop = RNA_def_property(srna, "buoyancy", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "buoyancy");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Buoyancy",
+ "Artificial buoyancy force in negative gravity direction based on pressure "
+ "differences inside the fluid");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* Factor flags */
+
+ prop = RNA_def_property(srna, "factor_repulsion", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_FAC_REPULSION);
+ RNA_def_property_ui_text(prop, "Factor Repulsion", "Repulsion is a factor of stiffness");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_factor_density", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_FAC_DENSITY);
+ RNA_def_property_ui_text(prop, "Factor Density",
+ "Density is calculated as a factor of default density (depends on particle size)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "factor_radius", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_FAC_RADIUS);
+ RNA_def_property_ui_text(prop, "Factor Radius", "Interaction radius is a factor of 4 * particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "factor_stiff_viscosity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_FAC_VISCOSITY);
+ RNA_def_property_ui_text(prop, "Factor Stiff Viscosity", "Stiff viscosity is a factor of normal viscosity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "factor_rest_length", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPH_FAC_REST_LENGTH);
+ RNA_def_property_ui_text(prop, "Factor Rest Length", "Spring rest length is a factor of 2 * particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+}
+
+static void rna_def_particle_settings_mtex(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem texco_items[] = {
+ {TEXCO_GLOB, "GLOBAL", 0, "Global", "Use global coordinates for the texture coordinates"},
+ {TEXCO_OBJECT, "OBJECT", 0, "Object", "Use linked object's coordinates for texture coordinates"},
+ {TEXCO_UV, "UV", 0, "UV", "Use UV coordinates for texture coordinates"},
+ {TEXCO_ORCO, "ORCO", 0, "Generated", "Use the original undeformed coordinates of the object"},
+ {TEXCO_STRAND, "STRAND", 0, "Strand / Particle",
+ "Use normalized strand texture coordinate (1D) or particle age (X) and trail position (Y)"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem prop_mapping_items[] = {
+ {MTEX_FLAT, "FLAT", 0, "Flat", "Map X and Y coordinates directly"},
+ {MTEX_CUBE, "CUBE", 0, "Cube", "Map using the normal vector"},
+ {MTEX_TUBE, "TUBE", 0, "Tube", "Map with Z as central axis"},
+ {MTEX_SPHERE, "SPHERE", 0, "Sphere", "Map with Z as central axis"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem prop_x_mapping_items[] = {
+ {0, "NONE", 0, "None", ""},
+ {1, "X", 0, "X", ""},
+ {2, "Y", 0, "Y", ""},
+ {3, "Z", 0, "Z", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem prop_y_mapping_items[] = {
+ {0, "NONE", 0, "None", ""},
+ {1, "X", 0, "X", ""},
+ {2, "Y", 0, "Y", ""},
+ {3, "Z", 0, "Z", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem prop_z_mapping_items[] = {
+ {0, "NONE", 0, "None", ""},
+ {1, "X", 0, "X", ""},
+ {2, "Y", 0, "Y", ""},
+ {3, "Z", 0, "Z", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "ParticleSettingsTextureSlot", "TextureSlot");
+ RNA_def_struct_sdna(srna, "MTex");
+ RNA_def_struct_ui_text(srna, "Particle Settings Texture Slot",
+ "Texture slot for textures in a Particle Settings data-block");
+
+ prop = RNA_def_property(srna, "texture_coords", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "texco");
+ RNA_def_property_enum_items(prop, texco_items);
+ RNA_def_property_ui_text(prop, "Texture Coordinates",
+ "Texture coordinates used to map the texture onto the background");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "object");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object to use for mapping with Object texture coordinates");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "uv_layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "uvname");
+ RNA_def_property_ui_text(prop, "UV Map", "UV map to use for mapping with UV texture coordinates");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "mapping_x", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "projx");
+ RNA_def_property_enum_items(prop, prop_x_mapping_items);
+ RNA_def_property_ui_text(prop, "X Mapping", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "mapping_y", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "projy");
+ RNA_def_property_enum_items(prop, prop_y_mapping_items);
+ RNA_def_property_ui_text(prop, "Y Mapping", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "mapping_z", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "projz");
+ RNA_def_property_enum_items(prop, prop_z_mapping_items);
+ RNA_def_property_ui_text(prop, "Z Mapping", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_mapping_items);
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* map to */
+ prop = RNA_def_property(srna, "use_map_time", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_TIME);
+ RNA_def_property_ui_text(prop, "Emission Time", "Affect the emission time of the particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_life", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_LIFE);
+ RNA_def_property_ui_text(prop, "Life Time", "Affect the life time of the particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_density", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_DENS);
+ RNA_def_property_ui_text(prop, "Density", "Affect the density of the particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_size", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_SIZE);
+ RNA_def_property_ui_text(prop, "Size", "Affect the particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_velocity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_IVEL);
+ RNA_def_property_ui_text(prop, "Initial Velocity", "Affect the particle initial velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_field", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_FIELD);
+ RNA_def_property_ui_text(prop, "Force Field", "Affect the particle force fields");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_gravity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_GRAVITY);
+ RNA_def_property_ui_text(prop, "Gravity", "Affect the particle gravity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_damp", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_DAMP);
+ RNA_def_property_ui_text(prop, "Damp", "Affect the particle velocity damping");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_map_clump", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_CLUMP);
+ RNA_def_property_ui_text(prop, "Clump", "Affect the child clumping");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_kink_amp", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_KINK_AMP);
+ RNA_def_property_ui_text(prop, "Kink Amplitude", "Affect the child kink amplitude");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_map_kink_freq", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_KINK_FREQ);
+ RNA_def_property_ui_text(prop, "Kink Frequency", "Affect the child kink frequency");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_map_rough", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_ROUGH);
+ RNA_def_property_ui_text(prop, "Rough", "Affect the child rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_map_length", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_LENGTH);
+ RNA_def_property_ui_text(prop, "Length", "Affect the child hair length");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+
+ /* influence factors */
+ prop = RNA_def_property(srna, "time_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "timefac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Emission Time Factor", "Amount texture affects particle emission time");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "life_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "lifefac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Life Time Factor", "Amount texture affects particle life time");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "density_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "padensfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Density Factor", "Amount texture affects particle density");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "size_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "sizefac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Size Factor", "Amount texture affects physical particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "velocity_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "ivelfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Velocity Factor", "Amount texture affects particle initial velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+
+ prop = RNA_def_property(srna, "field_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fieldfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Field Factor", "Amount texture affects particle force fields");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "gravity_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "gravityfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Gravity Factor", "Amount texture affects particle gravity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "damp_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "dampfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Damp Factor", "Amount texture affects particle damping");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+
+ prop = RNA_def_property(srna, "length_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "lengthfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Length Factor", "Amount texture affects child hair length");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "clump_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clumpfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Clump Factor", "Amount texture affects child clump");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_amp_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "kinkampfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Kink Amplitude Factor", "Amount texture affects child kink amplitude");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_freq_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "kinkfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Kink Frequency Factor", "Amount texture affects child kink frequency");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "rough_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "roughfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Rough Factor", "Amount texture affects child roughness");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+}
+
+static void rna_def_particle_settings(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem type_items[] = {
+ {PART_EMITTER, "EMITTER", 0, "Emitter", ""},
+ /*{PART_REACTOR, "REACTOR", 0, "Reactor", ""}, */
+ {PART_HAIR, "HAIR", 0, "Hair", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem phys_type_items[] = {
+ {PART_PHYS_NO, "NO", 0, "No", ""},
+ {PART_PHYS_NEWTON, "NEWTON", 0, "Newtonian", ""},
+ {PART_PHYS_KEYED, "KEYED", 0, "Keyed", ""},
+ {PART_PHYS_BOIDS, "BOIDS", 0, "Boids", ""},
+ {PART_PHYS_FLUID, "FLUID", 0, "Fluid", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem rot_mode_items[] = {
+ {0, "NONE", 0, "None", ""},
+ {PART_ROT_NOR, "NOR", 0, "Normal", ""},
+ {PART_ROT_NOR_TAN, "NOR_TAN", 0, "Normal-Tangent", ""},
+ {PART_ROT_VEL, "VEL", 0, "Velocity / Hair", ""},
+ {PART_ROT_GLOB_X, "GLOB_X", 0, "Global X", ""},
+ {PART_ROT_GLOB_Y, "GLOB_Y", 0, "Global Y", ""},
+ {PART_ROT_GLOB_Z, "GLOB_Z", 0, "Global Z", ""},
+ {PART_ROT_OB_X, "OB_X", 0, "Object X", ""},
+ {PART_ROT_OB_Y, "OB_Y", 0, "Object Y", ""},
+ {PART_ROT_OB_Z, "OB_Z", 0, "Object Z", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem ave_mode_items[] = {
+ {0, "NONE", 0, "None", ""},
+ {PART_AVE_VELOCITY, "VELOCITY", 0, "Velocity", ""},
+ {PART_AVE_HORIZONTAL, "HORIZONTAL", 0, "Horizontal", ""},
+ {PART_AVE_VERTICAL, "VERTICAL", 0, "Vertical", ""},
+ {PART_AVE_GLOBAL_X, "GLOBAL_X", 0, "Global X", ""},
+ {PART_AVE_GLOBAL_Y, "GLOBAL_Y", 0, "Global Y", ""},
+ {PART_AVE_GLOBAL_Z, "GLOBAL_Z", 0, "Global Z", ""},
+ {PART_AVE_RAND, "RAND", 0, "Random", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem react_event_items[] = {
+ {PART_EVENT_DEATH, "DEATH", 0, "Death", ""},
+ {PART_EVENT_COLLIDE, "COLLIDE", 0, "Collision", ""},
+ {PART_EVENT_NEAR, "NEAR", 0, "Near", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem child_type_items[] = {
+ {0, "NONE", 0, "None", ""},
+ {PART_CHILD_PARTICLES, "SIMPLE", 0, "Simple", ""},
+ {PART_CHILD_FACES, "INTERPOLATED", 0, "Interpolated", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /*TODO: names, tooltips */
+ static EnumPropertyItem integrator_type_items[] = {
+ {PART_INT_EULER, "EULER", 0, "Euler", ""},
+ {PART_INT_VERLET, "VERLET", 0, "Verlet", ""},
+ {PART_INT_MIDPOINT, "MIDPOINT", 0, "Midpoint", ""},
+ {PART_INT_RK4, "RK4", 0, "RK4", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem kink_type_items[] = {
+ {PART_KINK_NO, "NO", 0, "Nothing", ""},
+ {PART_KINK_CURL, "CURL", 0, "Curl", ""},
+ {PART_KINK_RADIAL, "RADIAL", 0, "Radial", ""},
+ {PART_KINK_WAVE, "WAVE", 0, "Wave", ""},
+ {PART_KINK_BRAID, "BRAID", 0, "Braid", ""},
+ {PART_KINK_SPIRAL, "SPIRAL", 0, "Spiral", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem bb_align_items[] = {
+ {PART_BB_X, "X", 0, "X", ""},
+ {PART_BB_Y, "Y", 0, "Y", ""},
+ {PART_BB_Z, "Z", 0, "Z", ""},
+ {PART_BB_VIEW, "VIEW", 0, "View", ""},
+ {PART_BB_VEL, "VEL", 0, "Velocity", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem bb_anim_items[] = {
+ {PART_BB_ANIM_NONE, "NONE", 0, "None", ""},
+ {PART_BB_ANIM_AGE, "AGE", 0, "Age", ""},
+ {PART_BB_ANIM_FRAME, "FRAME", 0, "Frame", ""},
+ {PART_BB_ANIM_ANGLE, "ANGLE", 0, "Angle", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem bb_split_offset_items[] = {
+ {PART_BB_OFF_NONE, "NONE", 0, "None", ""},
+ {PART_BB_OFF_LINEAR, "LINEAR", 0, "Linear", ""},
+ {PART_BB_OFF_RANDOM, "RANDOM", 0, "Random", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem draw_col_items[] = {
+ {PART_DRAW_COL_NONE, "NONE", 0, "None", ""},
+ {PART_DRAW_COL_MAT, "MATERIAL", 0, "Material", ""},
+ {PART_DRAW_COL_VEL, "VELOCITY", 0, "Velocity", ""},
+ {PART_DRAW_COL_ACC, "ACCELERATION", 0, "Acceleration", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem part_mat_items[] = {
+ {0, "DUMMY", 0, "Dummy", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "ParticleSettings", "ID");
+ RNA_def_struct_ui_text(srna, "Particle Settings", "Particle settings, reusable by multiple particle systems");
+ RNA_def_struct_ui_icon(srna, ICON_PARTICLE_DATA);
+
+ rna_def_mtex_common(brna, srna, "rna_ParticleSettings_mtex_begin", "rna_ParticleSettings_active_texture_get",
+ "rna_ParticleSettings_active_texture_set", NULL, "ParticleSettingsTextureSlot",
+ "ParticleSettingsTextureSlots", "rna_Particle_reset", NULL);
+
+ /* fluid particle type can't be checked from the type value in rna as it's not shown in the menu */
+ prop = RNA_def_property(srna, "is_fluid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_PartSettings_is_fluid_get", NULL);
+ RNA_def_property_ui_text(prop, "Fluid", "Particles were created by a fluid simulation");
+
+ /* flag */
+ prop = RNA_def_property(srna, "use_react_start_end", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_REACT_STA_END);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Start/End", "Give birth to unreacted particles eventually");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_react_multiple", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_REACT_MULTIPLE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Multi React", "React multiple times");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "regrow_hair", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_HAIR_REGROW);
+ RNA_def_property_ui_text(prop, "Regrow", "Regrow hair for each frame");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "show_unborn", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_UNBORN);
+ RNA_def_property_ui_text(prop, "Unborn", "Show particles before they are emitted");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_dead", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_DIED);
+ RNA_def_property_ui_text(prop, "Died", "Show particles after they have died");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_emit_random", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_TRAND);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Random", "Emit in random order of elements");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_even_distribution", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_EDISTR);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Even Distribution",
+ "Use even distribution from faces based on face areas or edge lengths");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_die_on_collision", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_DIE_ON_COL);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Die on hit", "Particles die when they collide with a deflector object");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_size_deflect", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_SIZE_DEFL);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Size Deflect", "Use particle's size in deflection");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_rotations", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_ROTATIONS);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Rotations", "Calculate particle rotations");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_dynamic_rotation", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_ROT_DYN);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Dynamic", "Particle rotations are affected by collisions and effectors");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_multiply_size_mass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_SIZEMASS);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Mass from Size", "Multiply mass by particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_advanced_hair", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", PART_HIDE_ADVANCED_HAIR);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Advanced", "Use full physics calculations for growing hair");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "lock_boids_to_surface", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_BOIDS_2D);
+ RNA_def_property_ui_text(prop, "Boids 2D", "Constrain boids to a surface");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_hair_bspline", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_HAIR_BSPLINE);
+ RNA_def_property_ui_text(prop, "B-Spline", "Interpolate hair using B-Splines");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "invert_grid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_GRID_INVERT);
+ RNA_def_property_ui_text(prop, "Invert Grid", "Invert what is considered object and what is not");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "hexagonal_grid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_GRID_HEXAGONAL);
+ RNA_def_property_ui_text(prop, "Hexagonal Grid", "Create the grid in a hexagonal pattern");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "apply_effector_to_children", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_CHILD_EFFECT);
+ RNA_def_property_ui_text(prop, "Effect Children", "Apply effectors to children");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "create_long_hair_children", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_CHILD_LONG_HAIR);
+ RNA_def_property_ui_text(prop, "Long Hair", "Calculate children that suit long hair well");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "apply_guide_to_children", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_CHILD_GUIDE);
+ RNA_def_property_ui_text(prop, "apply_guide_to_children", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_self_effect", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PART_SELF_EFFECT);
+ RNA_def_property_ui_text(prop, "Self Effect", "Particle effectors affect themselves");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Type", "Particle Type");
+ RNA_def_property_update(prop, 0, "rna_Particle_change_type");
+
+ prop = RNA_def_property(srna, "emit_from", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "from");
+ RNA_def_property_enum_items(prop, part_reactor_from_items);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_from_itemf");
+ RNA_def_property_ui_text(prop, "Emit From", "Where to emit particles from");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "distr");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, part_dist_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_dist_itemf");
+ RNA_def_property_ui_text(prop, "Distribution", "How to distribute particles on selected element");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* physics modes */
+ prop = RNA_def_property(srna, "physics_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "phystype");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, phys_type_items);
+ RNA_def_property_ui_text(prop, "Physics Type", "Particle physics type");
+ RNA_def_property_update(prop, 0, "rna_Particle_change_physics");
+
+ prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "rotmode");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, rot_mode_items);
+ RNA_def_property_ui_text(prop, "Orientation axis",
+ "Particle orientation axis (does not affect Explode modifier's results)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "angular_velocity_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "avemode");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, ave_mode_items);
+ RNA_def_property_ui_text(prop, "Angular Velocity Axis", "What axis is used to change particle rotation with time");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "react_event", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "reactevent");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, react_event_items);
+ RNA_def_property_ui_text(prop, "React On", "The event of target particles to react on");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /*draw flag*/
+ prop = RNA_def_property(srna, "show_guide_hairs", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_GUIDE_HAIRS);
+ RNA_def_property_ui_text(prop, "Guide Hairs", "Show guide hairs");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "show_hair_grid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_HAIR_GRID);
+ RNA_def_property_ui_text(prop, "Guide Hairs", "Show hair simulation grid");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "show_velocity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_VEL);
+ RNA_def_property_ui_text(prop, "Velocity", "Show particle velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "show_size", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_SIZE);
+ RNA_def_property_ui_text(prop, "Size", "Show particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_render_emitter", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_EMITTER);
+ RNA_def_property_ui_text(prop, "Emitter", "Render emitter Object also");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "show_health", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_HEALTH);
+ RNA_def_property_ui_text(prop, "Health", "Draw boid health");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_absolute_path_time", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_ABS_PATH_TIME);
+ RNA_def_property_ui_text(prop, "Absolute Path Time", "Path timing is in absolute frames");
+ RNA_def_property_update(prop, 0, "rna_Particle_abspathtime_update");
+
+ prop = RNA_def_property(srna, "use_parent_particles", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_PARENT);
+ RNA_def_property_ui_text(prop, "Parents", "Render parent particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "show_number", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_NUM);
+ RNA_def_property_ui_text(prop, "Number", "Show particle number");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_group_pick_random", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_RAND_GR);
+ RNA_def_property_ui_text(prop, "Pick Random", "Pick objects from group randomly");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_group_count", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_COUNT_GR);
+ RNA_def_property_ui_text(prop, "Use Count", "Use object multiple times in the same group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_global_dupli", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_GLOBAL_OB);
+ RNA_def_property_ui_text(prop, "Global", "Use object's global coordinates for duplication");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_rotation_dupli", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_ROTATE_OB);
+ RNA_def_property_ui_text(prop, "Rotation",
+ "Use object's rotation for duplication (global x-axis is aligned "
+ "particle rotation axis)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_scale_dupli", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "draw", PART_DRAW_NO_SCALE_OB);
+ RNA_def_property_ui_text(prop, "Scale", "Use object's scale for duplication");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_render_adaptive", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_REN_ADAPT);
+ RNA_def_property_ui_text(prop, "Adaptive render", "Draw steps of the particle path");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_velocity_length", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_VEL_LENGTH);
+ RNA_def_property_ui_text(prop, "Speed", "Multiply line length by particle speed");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_whole_group", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_WHOLE_GR);
+ RNA_def_property_ui_text(prop, "Whole Group", "Use whole group at once");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "use_strand_primitive", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_REN_STRAND);
+ RNA_def_property_ui_text(prop, "Strand render", "Use the strand primitive for rendering");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "draw_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "draw_as");
+ RNA_def_property_enum_items(prop, part_draw_as_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_draw_as_itemf");
+ RNA_def_property_ui_text(prop, "Particle Drawing", "How particles are drawn in viewport");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "render_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "ren_as");
+ RNA_def_property_enum_items(prop, part_ren_as_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_ren_as_itemf");
+ RNA_def_property_ui_text(prop, "Particle Rendering", "How particles are rendered");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "draw_color", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "draw_col");
+ RNA_def_property_enum_items(prop, draw_col_items);
+ RNA_def_property_ui_text(prop, "Draw Color", "Draw additional particle data as a color");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "draw_size", PROP_INT, PROP_PIXEL);
+ RNA_def_property_range(prop, 0, 1000);
+ RNA_def_property_ui_range(prop, 0, 100, 1, -1);
+ RNA_def_property_ui_text(prop, "Draw Size", "Size of particles on viewport in pixels (0=default)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "child_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "childtype");
+ RNA_def_property_enum_items(prop, child_type_items);
+ RNA_def_property_ui_text(prop, "Children From", "Create child particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "draw_step", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 10);
+ RNA_def_property_ui_range(prop, 0, 7, 1, -1);
+ RNA_def_property_ui_text(prop, "Steps", "How many steps paths are drawn with (power of 2)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "render_step", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "ren_step");
+ RNA_def_property_range(prop, 0, 20);
+ RNA_def_property_ui_range(prop, 0, 9, 1, -1);
+ RNA_def_property_ui_text(prop, "Render", "How many steps paths are rendered with (power of 2)");
+
+ prop = RNA_def_property(srna, "hair_step", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 2, 50);
+ RNA_def_property_ui_text(prop, "Segments", "Number of hair segments");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "bending_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "bending_random");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Bending Stiffness", "Random stiffness of hairs");
+ RNA_def_property_update(prop, 0, "rna_Particle_cloth_update");
+
+ /*TODO: not found in UI, readonly? */
+ prop = RNA_def_property(srna, "keys_step", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, SHRT_MAX); /*TODO:min,max */
+ RNA_def_property_ui_text(prop, "Keys Step", "");
+
+ /* adaptive path rendering */
+ prop = RNA_def_property(srna, "adaptive_angle", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "adapt_angle");
+ RNA_def_property_range(prop, 0, 45);
+ RNA_def_property_ui_text(prop, "Degrees", "How many degrees path has to curve to make another render segment");
+
+ prop = RNA_def_property(srna, "adaptive_pixel", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "adapt_pix");
+ RNA_def_property_range(prop, 0, 50);
+ RNA_def_property_ui_text(prop, "Pixel", "How many pixels path has to cover to make another render segment");
+
+ prop = RNA_def_property(srna, "draw_percentage", PROP_INT, PROP_PERCENTAGE);
+ RNA_def_property_int_sdna(prop, NULL, "disp");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Display", "Percentage of particles to display in 3D view");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "material", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "omat");
+ RNA_def_property_range(prop, 1, 32767);
+ RNA_def_property_ui_text(prop, "Material Index", "Index of material slot used for rendering particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "material_slot", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "omat");
+ RNA_def_property_enum_items(prop, part_mat_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Particle_Material_itemf");
+ RNA_def_property_ui_text(prop, "Material Slot", "Material slot used for rendering particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "integrator", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, integrator_type_items);
+ RNA_def_property_ui_text(prop, "Integration",
+ "Algorithm used to calculate physics, from the fastest to the "
+ "most stable/accurate: Midpoint, Euler, Verlet, RK4 (Old)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "kink", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, kink_type_items);
+ RNA_def_property_ui_text(prop, "Kink", "Type of periodic offset on the path");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items);
+ RNA_def_property_ui_text(prop, "Axis", "Which axis to use for offset");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* billboards */
+ prop = RNA_def_property(srna, "lock_billboard", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_BB_LOCK);
+ RNA_def_property_ui_text(prop, "Lock Billboard", "Lock the billboards align axis");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_align", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "bb_align");
+ RNA_def_property_enum_items(prop, bb_align_items);
+ RNA_def_property_ui_text(prop, "Align to", "In respect to what the billboards are aligned");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_uv_split", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "bb_uv_split");
+ RNA_def_property_range(prop, 1, 100);
+ RNA_def_property_ui_range(prop, 1, 10, 1, -1);
+ RNA_def_property_ui_text(prop, "UV Split", "Number of rows/columns to split UV coordinates for billboards");
+
+ prop = RNA_def_property(srna, "billboard_animation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "bb_anim");
+ RNA_def_property_enum_items(prop, bb_anim_items);
+ RNA_def_property_ui_text(prop, "Animate", "How to animate billboard textures");
+
+ prop = RNA_def_property(srna, "billboard_offset_split", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "bb_split_offset");
+ RNA_def_property_enum_items(prop, bb_split_offset_items);
+ RNA_def_property_ui_text(prop, "Offset", "How to offset billboard textures");
+
+ prop = RNA_def_property(srna, "billboard_tilt", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "bb_tilt");
+ RNA_def_property_range(prop, -1.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Tilt", "Tilt of the billboards");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "color_maximum", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "color_vec_max");
+ RNA_def_property_range(prop, 0.01f, 100.0f);
+ RNA_def_property_ui_text(prop, "Color Maximum", "Maximum length of the particle color vector");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_tilt_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "bb_rand_tilt");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Tilt", "Random tilt of the billboards");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_offset", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "bb_offset");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_range(prop, -100.0f, 100.0f);
+ RNA_def_property_ui_range(prop, -1.0, 1.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Billboard Offset", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_size", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "bb_size");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_range(prop, 0.001f, 10.0f);
+ RNA_def_property_ui_text(prop, "Billboard Scale", "Scale billboards relative to particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_velocity_head", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "bb_vel_head");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Billboard Velocity Head", "Scale billboards by velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "billboard_velocity_tail", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "bb_vel_tail");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Billboard Velocity Tail", "Scale billboards by velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ /* simplification */
+ prop = RNA_def_property(srna, "use_simplify", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "simplify_flag", PART_SIMPLIFY_ENABLE);
+ RNA_def_property_ui_text(prop, "Child Simplification",
+ "Remove child strands as the object becomes smaller on the screen");
+
+ prop = RNA_def_property(srna, "use_simplify_viewport", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "simplify_flag", PART_SIMPLIFY_VIEWPORT);
+ RNA_def_property_ui_text(prop, "Viewport", "");
+
+ prop = RNA_def_property(srna, "simplify_refsize", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "simplify_refsize");
+ RNA_def_property_range(prop, 1, SHRT_MAX);
+ RNA_def_property_ui_text(prop, "Reference Size", "Reference size in pixels, after which simplification begins");
+
+ prop = RNA_def_property(srna, "simplify_rate", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Rate", "Speed of simplification");
+
+ prop = RNA_def_property(srna, "simplify_transition", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Transition", "Transition period for fading out strands");
+
+ prop = RNA_def_property(srna, "simplify_viewport", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 0.999f);
+ RNA_def_property_ui_text(prop, "Rate", "Speed of Simplification");
+
+ /* general values */
+ prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "sta"); /*optional if prop names are the same */
+ RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_float_funcs(prop, NULL, "rna_PartSettings_start_set", NULL);
+ RNA_def_property_ui_text(prop, "Start", "Frame number to start emitting particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "end");
+ RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF);
+
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_float_funcs(prop, NULL, "rna_PartSettings_end_set", NULL);
+ RNA_def_property_ui_text(prop, "End", "Frame number to stop emitting particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_TIME);
+ RNA_def_property_range(prop, 1.0f, MAXFRAMEF);
+ RNA_def_property_ui_text(prop, "Lifetime", "Life span of the particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "lifetime_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "randlife");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random", "Give the particle life a random variation");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "time_tweak", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "timetweak");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_range(prop, 0, 10, 1, 3);
+ RNA_def_property_ui_text(prop, "Tweak", "A multiplier for physics timestep (1.0 means one frame = 1/25 seconds)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "timestep", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_funcs(prop, "rna_PartSettings_timestep_get", "rna_PartSetings_timestep_set", NULL);
+ RNA_def_property_range(prop, 0.0001, 100.0);
+ RNA_def_property_ui_range(prop, 0.01, 10, 1, 3);
+ RNA_def_property_ui_text(prop, "Timestep", "The simulation timestep per frame (seconds per frame)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_adaptive_subframes", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "time_flag", PART_TIME_AUTOSF);
+ RNA_def_property_ui_text(prop, "Automatic Subframes", "Automatically set the number of subframes");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "subframes", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 1000);
+ RNA_def_property_ui_text(prop, "Subframes",
+ "Subframes to simulate for improved stability and finer granularity simulations "
+ "(dt = timestep / (subframes + 1))");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "courant_target", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0001, 10);
+ RNA_def_property_float_default(prop, 0.1);
+ RNA_def_property_ui_text(prop, "Adaptive Subframe Threshold",
+ "The relative distance a particle can move before requiring more subframes "
+ "(target Courant number); 0.01-0.3 is the recommended range");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "jitter_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_float_sdna(prop, NULL, "jitfac");
+ RNA_def_property_range(prop, 0.0f, 2.0f);
+ RNA_def_property_ui_text(prop, "Amount", "Amount of jitter applied to the sampling");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "effect_hair", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "eff_hair");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Stiffness", "Hair stiffness for effectors");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "count", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "totpart");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ /* This limit is for those freaks who have the machine power to handle it. */
+ /* 10M particles take around 2.2 Gb of memory / disk space in saved file and */
+ /* each cached frame takes around 0.5 Gb of memory / disk space depending on cache mode. */
+ RNA_def_property_range(prop, 0, 10000000);
+ RNA_def_property_ui_range(prop, 0, 100000, 1, -1);
+ RNA_def_property_ui_text(prop, "Number", "Total number of particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "userjit", PROP_INT, PROP_UNSIGNED); /*TODO: can we get a better name for userjit? */
+ RNA_def_property_int_sdna(prop, NULL, "userjit");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, 0, 1000);
+ RNA_def_property_ui_text(prop, "P/F", "Emission locations / face (0 = automatic)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "grid_resolution", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "grid_res");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, 1, 250); /* ~15M particles in a cube (ouch!), but could be very usable in a plane */
+ RNA_def_property_ui_range(prop, 1, 50, 1, -1); /* ~100k particles in a cube */
+ RNA_def_property_ui_text(prop, "Resolution", "The resolution of the particle grid");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "grid_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "grid_rand");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Grid Randomness", "Add random offset to the grid locations");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "effector_amount", PROP_INT, PROP_UNSIGNED);
+ /* in theory PROP_ANIMATABLE perhaps should be cleared, but animating this can give some interesting results! */
+ RNA_def_property_range(prop, 0, 10000); /* 10000 effectors will bel SLOW, but who knows */
+ RNA_def_property_ui_range(prop, 0, 100, 1, -1);
+ RNA_def_property_ui_text(prop, "Effector Number", "How many particles are effectors (0 is all particles)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* initial velocity factors */
+ prop = RNA_def_property(srna, "normal_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "normfac"); /*optional if prop names are the same */
+ RNA_def_property_range(prop, -1000.0f, 1000.0f);
+ RNA_def_property_ui_range(prop, 0, 100, 1, 3);
+ RNA_def_property_ui_text(prop, "Normal", "Let the surface normal give the particle a starting velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "object_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "obfac");
+ RNA_def_property_range(prop, -200.0f, 200.0f);
+ RNA_def_property_ui_range(prop, -1.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Object", "Let the object give the particle a starting velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "factor_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "randfac"); /*optional if prop names are the same */
+ RNA_def_property_range(prop, 0.0f, 200.0f);
+ RNA_def_property_ui_range(prop, 0, 100, 1, 3);
+ RNA_def_property_ui_text(prop, "Random", "Give the starting velocity a random variation");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "particle_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "partfac");
+ RNA_def_property_range(prop, -200.0f, 200.0f);
+ RNA_def_property_ui_range(prop, -1.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Particle", "Let the target particle give the particle a starting velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "tangent_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "tanfac");
+ RNA_def_property_range(prop, -1000.0f, 1000.0f);
+ RNA_def_property_ui_range(prop, -100, 100, 1, 2);
+ RNA_def_property_ui_text(prop, "Tangent", "Let the surface tangent give the particle a starting velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "tangent_phase", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "tanphase");
+ RNA_def_property_range(prop, -1.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Rot", "Rotate the surface tangent");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "reactor_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "reactfac");
+ RNA_def_property_range(prop, -10.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Reactor",
+ "Let the vector away from the target particle's location give the particle "
+ "a starting velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "object_align_factor", PROP_FLOAT, PROP_VELOCITY);
+ RNA_def_property_float_sdna(prop, NULL, "ob_vel");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_range(prop, -200.0f, 200.0f);
+ RNA_def_property_ui_range(prop, -100, 100, 1, 3);
+ RNA_def_property_ui_text(prop, "Object Aligned",
+ "Let the emitter object orientation give the particle a starting velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "angular_velocity_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "avefac");
+ RNA_def_property_range(prop, -200.0f, 200.0f);
+ RNA_def_property_ui_range(prop, -100, 100, 10, 3);
+ RNA_def_property_ui_text(prop, "Angular Velocity", "Angular velocity amount (in radians per second)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "phase_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "phasefac");
+ RNA_def_property_range(prop, -1.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Phase", "Rotation around the chosen orientation axis");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "rotation_factor_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "randrotfac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Orientation", "Randomize particle orientation");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "phase_factor_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "randphasefac");
+ RNA_def_property_range(prop, 0.0f, 2.0f);
+ RNA_def_property_ui_text(prop, "Random Phase", "Randomize rotation around the chosen orientation axis");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "hair_length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_funcs(prop, "rna_PartSetting_hairlength_get", "rna_PartSetting_hairlength_set", NULL);
+ RNA_def_property_range(prop, 0.0f, 1000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
+ RNA_def_property_ui_text(prop, "Hair Length", "Length of the hair");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* physical properties */
+ prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.00000001f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01, 100, 1, 3);
+ RNA_def_property_ui_text(prop, "Mass", "Mass of the particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "particle_size", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "size");
+ RNA_def_property_range(prop, 0.001f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01, 100, 1, 3);
+ RNA_def_property_ui_text(prop, "Size", "The size of the particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "size_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "randsize");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Size", "Give the particle size a random variation");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset_dependency");
+
+ /* global physical properties */
+ prop = RNA_def_property(srna, "drag_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "dragfac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Drag", "Amount of air-drag");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "brownian_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "brownfac");
+ RNA_def_property_range(prop, 0.0f, 200.0f);
+ RNA_def_property_ui_range(prop, 0, 20, 1, 3);
+ RNA_def_property_ui_text(prop, "Brownian", "Amount of random, erratic particle movement");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "damping", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "dampfac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Damp", "Amount of damping");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* random length */
+ prop = RNA_def_property(srna, "length_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "randlength");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Length", "Give path length a random variation");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ /* children */
+ prop = RNA_def_property(srna, "child_nbr", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "child_nbr"); /*optional if prop names are the same */
+ RNA_def_property_range(prop, 0, 100000);
+ RNA_def_property_ui_range(prop, 0, 1000, 1, -1);
+ RNA_def_property_ui_text(prop, "Children Per Parent", "Number of children/parent");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "rendered_child_count", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "ren_child_nbr");
+ RNA_def_property_range(prop, 0, 100000);
+ RNA_def_property_ui_range(prop, 0, 10000, 1, -1);
+ RNA_def_property_ui_text(prop, "Rendered Children", "Number of children/parent for rendering");
+
+ prop = RNA_def_property(srna, "virtual_parents", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "parents");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Virtual Parents", "Relative amount of virtual parents");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_size", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "childsize");
+ RNA_def_property_range(prop, 0.001f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01f, 100.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Child Size", "A multiplier for the child particle size");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_size_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "childrandsize");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Random Child Size", "Random variation to the size of the child particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_radius", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "childrad");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Child Radius", "Radius of children around parent");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_roundness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "childflat");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Child Roundness", "Roundness of children around parent");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* clumping */
+ prop = RNA_def_property(srna, "clump_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clumpfac");
+ RNA_def_property_range(prop, -1.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Clump", "Amount of clumping");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "clump_shape", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clumppow");
+ RNA_def_property_range(prop, -0.999f, 0.999f);
+ RNA_def_property_ui_text(prop, "Shape", "Shape of clumping");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_clump_curve", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "child_flag", PART_CHILD_USE_CLUMP_CURVE);
+ RNA_def_property_ui_text(prop, "Use Clump Curve", "Use a curve to define clump tapering");
+ RNA_def_property_update(prop, 0, "rna_ParticleSettings_use_clump_curve_update");
+
+ prop = RNA_def_property(srna, "clump_curve", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "clumpcurve");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Clump Curve", "Curve defining clump tapering");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_clump_noise", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "child_flag", PART_CHILD_USE_CLUMP_NOISE);
+ RNA_def_property_ui_text(prop, "Use Clump Noise", "Create random clumps around the parent");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "clump_noise_size", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clump_noise_size");
+ RNA_def_property_range(prop, 0.00001f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.1f, 3);
+ RNA_def_property_ui_text(prop, "Clump Noise Size", "Size of clump noise");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* kink */
+ prop = RNA_def_property(srna, "kink_amplitude", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "kink_amp");
+ RNA_def_property_range(prop, -100000.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Amplitude", "The amplitude of the offset");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_amplitude_clump", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "kink_amp_clump");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Amplitude Clump", "How much clump affects kink amplitude");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_amplitude_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "kink_amp_random");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Amplitude Random", "Random variation of the amplitude");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_frequency", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "kink_freq");
+ RNA_def_property_range(prop, -100000.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Frequency", "The frequency of the offset (1/total length)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_shape", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, -0.999f, 0.999f);
+ RNA_def_property_ui_text(prop, "Shape", "Adjust the offset to the beginning/end");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_flat", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Flatness", "How flat the hairs are");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_extra_steps", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 1, INT_MAX);
+ RNA_def_property_ui_range(prop, 1, 100, 1, -1);
+ RNA_def_property_ui_text(prop, "Extra Steps", "Extra steps for resolution of special kink features");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "kink_axis_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Axis Random", "Random variation of the orientation");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* rough */
+ prop = RNA_def_property(srna, "roughness_1", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough1");
+ RNA_def_property_range(prop, 0.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Rough1", "Amount of location dependent rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "roughness_1_size", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough1_size");
+ RNA_def_property_range(prop, 0.01f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Size1", "Size of location dependent rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "roughness_2", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough2");
+ RNA_def_property_range(prop, 0.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Rough2", "Amount of random rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "roughness_2_size", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough2_size");
+ RNA_def_property_range(prop, 0.01f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Size2", "Size of random rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "roughness_2_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough2_thres");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Threshold", "Amount of particles left untouched by random rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "roughness_endpoint", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough_end");
+ RNA_def_property_range(prop, 0.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Rough Endpoint", "Amount of end point rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "roughness_end_shape", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "rough_end_shape");
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Shape", "Shape of end point rough");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "use_roughness_curve", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "child_flag", PART_CHILD_USE_ROUGH_CURVE);
+ RNA_def_property_ui_text(prop, "Use Roughness Curve", "Use a curve to define roughness");
+ RNA_def_property_update(prop, 0, "rna_ParticleSettings_use_roughness_curve_update");
+
+ prop = RNA_def_property(srna, "roughness_curve", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "roughcurve");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Roughness Curve", "Curve defining roughness");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clength");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Length", "Length of child paths");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_length_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clength_thres");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Threshold", "Amount of particles left untouched by child path length");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* parting */
+ prop = RNA_def_property(srna, "child_parting_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "parting_fac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Parting Factor", "Create parting in the children based on parent strands");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_parting_min", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "parting_min");
+ RNA_def_property_range(prop, 0.0f, 180.0f);
+ RNA_def_property_ui_text(prop, "Parting Minimum",
+ "Minimum root to tip angle (tip distance/root distance for long hair)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "child_parting_max", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "parting_max");
+ RNA_def_property_range(prop, 0.0f, 180.0f);
+ RNA_def_property_ui_text(prop, "Parting Maximum",
+ "Maximum root to tip angle (tip distance/root distance for long hair)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* branching */
+ prop = RNA_def_property(srna, "branch_threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "branch_thres");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Threshold", "Threshold of branching");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* drawing stuff */
+ prop = RNA_def_property(srna, "line_length_tail", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_funcs(prop, "rna_PartSetting_linelentail_get", "rna_PartSetting_linelentail_set", NULL);
+ RNA_def_property_range(prop, 0.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Tail", "Length of the line's tail");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "line_length_head", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_funcs(prop, "rna_PartSetting_linelenhead_get", "rna_PartSetting_linelenhead_set", NULL);
+ RNA_def_property_range(prop, 0.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Head", "Length of the line's head");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "path_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "path_start");
+ RNA_def_property_float_funcs(prop, NULL, NULL, "rna_PartSetting_pathstartend_range");
+ RNA_def_property_ui_text(prop, "Path Start", "Starting time of drawn path");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "path_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "path_end");
+ RNA_def_property_float_funcs(prop, NULL, NULL, "rna_PartSetting_pathstartend_range");
+ RNA_def_property_ui_text(prop, "Path End", "End time of drawn path");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "trail_count", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "trail_count");
+ RNA_def_property_range(prop, 1, 100000);
+ RNA_def_property_ui_range(prop, 1, 100, 1, -1);
+ RNA_def_property_ui_text(prop, "Trail Count", "Number of trail particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ /* keyed particles */
+ prop = RNA_def_property(srna, "keyed_loops", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "keyed_loops");
+ RNA_def_property_range(prop, 1.0f, 10000.0f);
+ RNA_def_property_ui_range(prop, 1.0f, 100.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Loop count", "Number of times the keys are looped");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ /* modified dm support */
+ prop = RNA_def_property(srna, "use_modifier_stack", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_modifier_stack", 0);
+ RNA_def_property_ui_text(prop, "Use Modifier Stack", "Emit particles from mesh with modifiers applied "
+ "(must use same subsurf level for viewport and render for correct results)");
+ RNA_def_property_update(prop, 0, "rna_Particle_change_type");
+
+ /* draw objects & groups */
+ prop = RNA_def_property(srna, "dupli_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "dup_group");
+ RNA_def_property_struct_type(prop, "Group");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Dupli Group", "Show Objects in this Group in place of particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "dupli_weights", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "dupliweights", NULL);
+ RNA_def_property_struct_type(prop, "ParticleDupliWeight");
+ RNA_def_property_ui_text(prop, "Dupli Group Weights", "Weights for all of the objects in the dupli group");
+
+ prop = RNA_def_property(srna, "active_dupliweight", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ParticleDupliWeight");
+ RNA_def_property_pointer_funcs(prop, "rna_ParticleDupliWeight_active_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Dupli Object", "");
+
+ prop = RNA_def_property(srna, "active_dupliweight_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_ParticleDupliWeight_active_index_get",
+ "rna_ParticleDupliWeight_active_index_set",
+ "rna_ParticleDupliWeight_active_index_range");
+ RNA_def_property_ui_text(prop, "Active Dupli Object Index", "");
+
+ prop = RNA_def_property(srna, "dupli_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "dup_ob");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Dupli Object", "Show this Object in place of particles");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_dependency");
+
+ prop = RNA_def_property(srna, "billboard_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "bb_ob");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Billboard Object", "Billboards face this object (default is active camera)");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ /* boids */
+ prop = RNA_def_property(srna, "boids", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "BoidSettings");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Boid Settings", "");
+
+ /* Fluid particles */
+ prop = RNA_def_property(srna, "fluid", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SPHFluidSettings");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "SPH Fluid Settings", "");
+
+ /* Effector weights */
+ prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "EffectorWeights");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Effector Weights", "");
+
+ /* animation here? */
+ rna_def_animdata_common(srna);
+
+ prop = RNA_def_property(srna, "force_field_1", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "pd");
+ RNA_def_property_struct_type(prop, "FieldSettings");
+ RNA_def_property_pointer_funcs(prop, "rna_Particle_field1_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Force Field 1", "");
+
+ prop = RNA_def_property(srna, "force_field_2", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "pd2");
+ RNA_def_property_struct_type(prop, "FieldSettings");
+ RNA_def_property_pointer_funcs(prop, "rna_Particle_field2_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Force Field 2", "");
+}
+
+static void rna_def_particle_target(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {PTARGET_MODE_FRIEND, "FRIEND", 0, "Friend", ""},
+ {PTARGET_MODE_NEUTRAL, "NEUTRAL", 0, "Neutral", ""},
+ {PTARGET_MODE_ENEMY, "ENEMY", 0, "Enemy", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+
+ srna = RNA_def_struct(brna, "ParticleTarget", NULL);
+ RNA_def_struct_ui_text(srna, "Particle Target", "Target particle system");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleTarget_name_get", "rna_ParticleTarget_name_length", NULL);
+ RNA_def_property_ui_text(prop, "Name", "Particle target name");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "ob");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Target Object",
+ "The object that has the target particle system (empty if same object)");
+ RNA_def_property_update(prop, 0, "rna_Particle_target_reset");
+
+ prop = RNA_def_property(srna, "system", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "psys");
+ RNA_def_property_range(prop, 1, INT_MAX);
+ RNA_def_property_ui_text(prop, "Target Particle System", "The index of particle system on the target object");
+ RNA_def_property_update(prop, 0, "rna_Particle_target_reset");
+
+ prop = RNA_def_property(srna, "time", PROP_FLOAT, PROP_TIME);
+ RNA_def_property_float_sdna(prop, NULL, "time");
+ RNA_def_property_range(prop, 0.0, 30000.0f); /*TODO: replace 30000 with MAXFRAMEF when available in 2.5 */
+ RNA_def_property_ui_text(prop, "Time", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_target_redo");
+
+ prop = RNA_def_property(srna, "duration", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "duration");
+ RNA_def_property_range(prop, 0.0, 30000.0f); /*TODO: replace 30000 with MAXFRAMEF when available in 2.5 */
+ RNA_def_property_ui_text(prop, "Duration", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_target_redo");
+
+ prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PTARGET_VALID);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Valid", "Keyed particles target is valid");
+
+ prop = RNA_def_property(srna, "alliance", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Mode", "");
+ RNA_def_property_update(prop, 0, "rna_Particle_target_reset");
+
+}
+static void rna_def_particle_system(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ static EnumPropertyItem resolution_items[] = {
+ {eModifierMode_Realtime, "PREVIEW", 0, "Preview", "Apply modifier preview settings"},
+ {eModifierMode_Render, "RENDER", 0, "Render", "Apply modifier render settings"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "ParticleSystem", NULL);
+ RNA_def_struct_ui_text(srna, "Particle System", "Particle system in an object");
+ RNA_def_struct_ui_icon(srna, ICON_PARTICLE_DATA);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Particle system name");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL);
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_ParticleSystem_name_set");
+ RNA_def_struct_name_property(srna, prop);
+
+ /* access to particle settings is redirected through functions */
+ /* to allow proper id-buttons functionality */
+ prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE);
+ /*RNA_def_property_pointer_sdna(prop, NULL, "part"); */
+ RNA_def_property_struct_type(prop, "ParticleSettings");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
+ RNA_def_property_pointer_funcs(prop, "rna_particle_settings_get", "rna_particle_settings_set", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Settings", "Particle system settings");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "particles", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "particles", "totpart");
+ RNA_def_property_struct_type(prop, "Particle");
+ RNA_def_property_ui_text(prop, "Particles", "Particles generated by the particle system");
+
+ prop = RNA_def_property(srna, "child_particles", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "child", "totchild");
+ RNA_def_property_struct_type(prop, "ChildParticle");
+ RNA_def_property_ui_text(prop, "Child Particles", "Child particles generated by the particle system");
+
+ prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Seed", "Offset in the random number table, to get a different randomized result");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "child_seed", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Child Seed",
+ "Offset in the random number table for child particles, to get a different "
+ "randomized result");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ /* hair */
+ prop = RNA_def_property(srna, "is_global_hair", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PSYS_GLOBAL_HAIR);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Global Hair", "Hair keys are in global coordinate space");
+
+ prop = RNA_def_property(srna, "use_hair_dynamics", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PSYS_HAIR_DYNAMICS);
+ RNA_def_property_ui_text(prop, "Hair Dynamics", "Enable hair dynamics using cloth simulation");
+ RNA_def_property_update(prop, 0, "rna_Particle_hair_dynamics");
+
+ prop = RNA_def_property(srna, "cloth", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "clmd");
+ RNA_def_property_struct_type(prop, "ClothModifier");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Cloth", "Cloth dynamics for hair");
+
+ /* reactor */
+ prop = RNA_def_property(srna, "reactor_target_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "target_ob");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Reactor Target Object",
+ "For reactor systems, the object that has the target particle system "
+ "(empty if same object)");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "reactor_target_particle_system", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "target_psys");
+ RNA_def_property_range(prop, 1, SHRT_MAX);
+ RNA_def_property_ui_text(prop, "Reactor Target Particle System",
+ "For reactor systems, index of particle system on the target object");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* keyed */
+ prop = RNA_def_property(srna, "use_keyed_timing", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PSYS_KEYED_TIMING);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Keyed timing", "Use key times");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ParticleTarget");
+ RNA_def_property_ui_text(prop, "Targets", "Target particle systems");
+
+ prop = RNA_def_property(srna, "active_particle_target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ParticleTarget");
+ RNA_def_property_pointer_funcs(prop, "rna_ParticleSystem_active_particle_target_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Particle Target", "");
+
+ prop = RNA_def_property(srna, "active_particle_target_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_ParticleSystem_active_particle_target_index_get",
+ "rna_ParticleSystem_active_particle_target_index_set",
+ "rna_ParticleSystem_active_particle_target_index_range");
+ RNA_def_property_ui_text(prop, "Active Particle Target Index", "");
+
+ /* billboard */
+ prop = RNA_def_property(srna, "billboard_normal_uv", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "bb_uvname[0]");
+ RNA_def_property_string_maxlength(prop, 32);
+ RNA_def_property_ui_text(prop, "Billboard Normal UV", "UV map to control billboard normals");
+
+ prop = RNA_def_property(srna, "billboard_time_index_uv", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "bb_uvname[1]");
+ RNA_def_property_string_maxlength(prop, 32);
+ RNA_def_property_ui_text(prop, "Billboard Time Index UV", "UV map to control billboard time index (X-Y)");
+
+ prop = RNA_def_property(srna, "billboard_split_uv", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "bb_uvname[2]");
+ RNA_def_property_string_maxlength(prop, 32);
+ RNA_def_property_ui_text(prop, "Billboard Split UV", "UV map to control billboard splitting");
+
+ /* vertex groups */
+
+ /* note, internally store as ints, access as strings */
+#if 0 /* int access. works ok but isn't useful for the UI */
+ prop = RNA_def_property(srna, "vertex_group_density", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "vgroup[0]");
+ RNA_def_property_ui_text(prop, "Vertex Group Density", "Vertex group to control density");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+#endif
+
+ prop = RNA_def_property(srna, "vertex_group_density", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_0", "rna_ParticleVGroup_name_len_0",
+ "rna_ParticleVGroup_name_set_0");
+ RNA_def_property_ui_text(prop, "Vertex Group Density", "Vertex group to control density");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_density", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_DENSITY));
+ RNA_def_property_ui_text(prop, "Vertex Group Density Negate", "Negate the effect of the density vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "vertex_group_velocity", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_1", "rna_ParticleVGroup_name_len_1",
+ "rna_ParticleVGroup_name_set_1");
+ RNA_def_property_ui_text(prop, "Vertex Group Velocity", "Vertex group to control velocity");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_velocity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_VEL));
+ RNA_def_property_ui_text(prop, "Vertex Group Velocity Negate", "Negate the effect of the velocity vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "vertex_group_length", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_2", "rna_ParticleVGroup_name_len_2",
+ "rna_ParticleVGroup_name_set_2");
+ RNA_def_property_ui_text(prop, "Vertex Group Length", "Vertex group to control length");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_length", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_LENGTH));
+ RNA_def_property_ui_text(prop, "Vertex Group Length Negate", "Negate the effect of the length vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ prop = RNA_def_property(srna, "vertex_group_clump", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_3", "rna_ParticleVGroup_name_len_3",
+ "rna_ParticleVGroup_name_set_3");
+ RNA_def_property_ui_text(prop, "Vertex Group Clump", "Vertex group to control clump");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_clump", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_CLUMP));
+ RNA_def_property_ui_text(prop, "Vertex Group Clump Negate", "Negate the effect of the clump vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "vertex_group_kink", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_4", "rna_ParticleVGroup_name_len_4",
+ "rna_ParticleVGroup_name_set_4");
+ RNA_def_property_ui_text(prop, "Vertex Group Kink", "Vertex group to control kink");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_kink", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_KINK));
+ RNA_def_property_ui_text(prop, "Vertex Group Kink Negate", "Negate the effect of the kink vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "vertex_group_roughness_1", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_5", "rna_ParticleVGroup_name_len_5",
+ "rna_ParticleVGroup_name_set_5");
+ RNA_def_property_ui_text(prop, "Vertex Group Roughness 1", "Vertex group to control roughness 1");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_roughness_1", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_ROUGH1));
+ RNA_def_property_ui_text(prop, "Vertex Group Roughness 1 Negate",
+ "Negate the effect of the roughness 1 vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "vertex_group_roughness_2", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_6", "rna_ParticleVGroup_name_len_6",
+ "rna_ParticleVGroup_name_set_6");
+ RNA_def_property_ui_text(prop, "Vertex Group Roughness 2", "Vertex group to control roughness 2");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_roughness_2", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_ROUGH2));
+ RNA_def_property_ui_text(prop, "Vertex Group Roughness 2 Negate",
+ "Negate the effect of the roughness 2 vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "vertex_group_roughness_end", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_7", "rna_ParticleVGroup_name_len_7",
+ "rna_ParticleVGroup_name_set_7");
+ RNA_def_property_ui_text(prop, "Vertex Group Roughness End", "Vertex group to control roughness end");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_roughness_end", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_ROUGHE));
+ RNA_def_property_ui_text(prop, "Vertex Group Roughness End Negate",
+ "Negate the effect of the roughness end vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "vertex_group_size", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_8", "rna_ParticleVGroup_name_len_8",
+ "rna_ParticleVGroup_name_set_8");
+ RNA_def_property_ui_text(prop, "Vertex Group Size", "Vertex group to control size");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_size", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_SIZE));
+ RNA_def_property_ui_text(prop, "Vertex Group Size Negate", "Negate the effect of the size vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "vertex_group_tangent", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_9", "rna_ParticleVGroup_name_len_9",
+ "rna_ParticleVGroup_name_set_9");
+ RNA_def_property_ui_text(prop, "Vertex Group Tangent", "Vertex group to control tangent");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_tangent", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_TAN));
+ RNA_def_property_ui_text(prop, "Vertex Group Tangent Negate", "Negate the effect of the tangent vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "vertex_group_rotation", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_10", "rna_ParticleVGroup_name_len_10",
+ "rna_ParticleVGroup_name_set_10");
+ RNA_def_property_ui_text(prop, "Vertex Group Rotation", "Vertex group to control rotation");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_rotation", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_ROT));
+ RNA_def_property_ui_text(prop, "Vertex Group Rotation Negate", "Negate the effect of the rotation vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "vertex_group_field", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_ParticleVGroup_name_get_11", "rna_ParticleVGroup_name_len_11",
+ "rna_ParticleVGroup_name_set_11");
+ RNA_def_property_ui_text(prop, "Vertex Group Field", "Vertex group to control field");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "invert_vertex_group_field", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "vg_neg", (1 << PSYS_VG_EFFECTOR));
+ RNA_def_property_ui_text(prop, "Vertex Group Field Negate", "Negate the effect of the field vertex group");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ /* pointcache */
+ prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "pointcache");
+ RNA_def_property_struct_type(prop, "PointCache");
+ RNA_def_property_ui_text(prop, "Point Cache", "");
+
+ prop = RNA_def_property(srna, "has_multiple_caches", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_ParticleSystem_multiple_caches_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Multiple Caches", "Particle system has multiple point caches");
+
+ /* offset ob */
+ prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "parent");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Parent",
+ "Use this object's coordinate system instead of global coordinate system");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo");
+
+ /* hair or cache editing */
+ prop = RNA_def_property(srna, "is_editable", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_ParticleSystem_editable_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Editable", "Particle system can be edited in particle mode");
+
+ prop = RNA_def_property(srna, "is_edited", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_ParticleSystem_edited_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Edited", "Particle system has been edited in particle mode");
+
+ /* Read-only: this is calculated internally. Changing it would only affect
+ * the next time-step. The user should change ParticlSettings.subframes or
+ * ParticleSettings.courant_target instead. */
+ prop = RNA_def_property(srna, "dt_frac", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 1.0f / 101.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Timestep", "The current simulation time step size, as a fraction of a frame");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ RNA_def_struct_path_func(srna, "rna_ParticleSystem_path");
+
+ /* set viewport or render resolution */
+ func = RNA_def_function(srna, "set_resolution", "rna_ParticleSystem_set_resolution");
+ RNA_def_function_ui_description(func, "Set the resolution to use for the number of particles");
+ RNA_def_pointer(func, "scene", "Scene", "", "Scene");
+ RNA_def_pointer(func, "object", "Object", "", "Object");
+ RNA_def_enum(func, "resolution", resolution_items, 0, "", "Resolution settings to apply");
+
+ /* extract cached hair location data */
+ func = RNA_def_function(srna, "co_hair", "rna_ParticleSystem_co_hair");
+ RNA_def_function_ui_description(func, "Obtain cache hair data");
+ parm = RNA_def_pointer(func, "object", "Object", "", "Object");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_int(func, "particle_no", 0, INT_MIN, INT_MAX, "Particle no", "", INT_MIN, INT_MAX);
+ RNA_def_int(func, "step", 0, INT_MIN, INT_MAX, "step no", "", INT_MIN, INT_MAX);
+ parm = RNA_def_float_vector(func, "co", 3, NULL, -FLT_MAX, FLT_MAX, "Co",
+ "Exported hairkey location", -1e4, 1e4);
+ RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_function_output(func, parm);
+
+ /* extract hair UVs */
+ func = RNA_def_function(srna, "uv_on_emitter", "rna_ParticleSystem_uv_on_emitter");
+ RNA_def_function_ui_description(func, "Obtain uv for all particles");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "modifier", "ParticleSystemModifier", "", "Particle modifier");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ prop = RNA_def_pointer(func, "particle", "Particle", "", "Particle");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_int(func, "particle_no", 0, INT_MIN, INT_MAX, "Particle no", "", INT_MIN, INT_MAX);
+ RNA_def_int(func, "uv_no", 0, INT_MIN, INT_MAX, "UV no", "", INT_MIN, INT_MAX);
+ parm = RNA_def_property(func, "uv", PROP_FLOAT, PROP_COORDS);
+ RNA_def_property_array(parm, 2);
+ RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_function_output(func, parm);
+
+ /* extract hair mcols */
+ func = RNA_def_function(srna, "mcol_on_emitter", "rna_ParticleSystem_mcol_on_emitter");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Obtain mcol for all particles");
+ parm = RNA_def_pointer(func, "modifier", "ParticleSystemModifier", "", "Particle modifier");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "particle", "Particle", "", "Particle");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_int(func, "particle_no", 0, INT_MIN, INT_MAX, "Particle no", "", INT_MIN, INT_MAX);
+ RNA_def_int(func, "vcol_no", 0, INT_MIN, INT_MAX, "vcol no", "", INT_MIN, INT_MAX);
+ parm = RNA_def_property(func, "mcol", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_array(parm, 3);
+ RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_function_output(func, parm);
+
+}
+
+void RNA_def_particle(BlenderRNA *brna)
+{
+ rna_def_particle_target(brna);
+ rna_def_fluid_settings(brna);
+ rna_def_particle_hair_key(brna);
+ rna_def_particle_key(brna);
+
+ rna_def_child_particle(brna);
+ rna_def_particle(brna);
+ rna_def_particle_dupliweight(brna);
+ rna_def_particle_system(brna);
+ rna_def_particle_settings_mtex(brna);
+ rna_def_particle_settings(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c
index be835238911..a1a7efdaba5 100644
--- a/source/blender/makesrna/intern/rna_rigidbody.c
+++ b/source/blender/makesrna/intern/rna_rigidbody.c
@@ -109,7 +109,8 @@ static EnumPropertyItem rigidbody_mesh_source_items[] = {
static void rna_RigidBodyWorld_reset(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
RigidBodyWorld *rbw = (RigidBodyWorld *)ptr->data;
- UNUSED_VARS(rbw);
+
+ BKE_rigidbody_cache_reset(rbw);
}
static char *rna_RigidBodyWorld_path(PointerRNA *UNUSED(ptr))
@@ -148,10 +149,10 @@ static void rna_RigidBodyWorld_split_impulse_set(PointerRNA *ptr, int value)
static void rna_RigidBodyOb_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
RigidBodyWorld *rbw = scene->rigidbody_world;
- UNUSED_VARS(rbw);
+
+ BKE_rigidbody_cache_reset(rbw);
}
-
static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Object *ob = ptr->id.data;
@@ -161,10 +162,12 @@ static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA *
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
}
-static void rna_RigidBodyOb_shape_reset(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+static void rna_RigidBodyOb_shape_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
{
+ RigidBodyWorld *rbw = scene->rigidbody_world;
RigidBodyOb *rbo = (RigidBodyOb *)ptr->data;
+ BKE_rigidbody_cache_reset(rbw);
if (rbo->physics_shape)
rbo->flag |= RBO_FLAG_NEEDS_RESHAPE;
}
@@ -796,6 +799,12 @@ static void rna_def_rigidbody_world(BlenderRNA *brna)
"stability a little so use only when necessary)");
RNA_def_property_update(prop, NC_SCENE, "rna_RigidBodyWorld_reset");
+ /* cache */
+ prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "pointcache");
+ RNA_def_property_ui_text(prop, "Point Cache", "");
+
/* effector weights */
prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "EffectorWeights");
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index b4201dd296e..ddfb5dc6d61 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -29,6 +29,7 @@
#include "DNA_brush_types.h"
#include "DNA_group_types.h"
#include "DNA_modifier_types.h"
+#include "DNA_particle_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_linestyle_types.h"
@@ -423,6 +424,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = {
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_depsgraph.h"
#include "BKE_mesh.h"
@@ -1652,6 +1654,15 @@ static void rna_Scene_use_nodes_update(bContext *C, PointerRNA *ptr)
ED_node_composit_default(C, scene);
}
+static void rna_Physics_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Scene *scene = (Scene *)ptr->id.data;
+ Base *base;
+
+ for (base = scene->base.first; base; base = base->next)
+ BKE_ptcache_object_reset(scene, base->object, PTCACHE_RESET_DEPSGRAPH);
+}
+
static void rna_Scene_editmesh_select_mode_set(PointerRNA *ptr, const int *value)
{
Scene *scene = (Scene *)ptr->id.data;
@@ -1688,6 +1699,7 @@ static void rna_Scene_editmesh_select_mode_update(Main *UNUSED(bmain), Scene *sc
static void object_simplify_update(Object *ob)
{
ModifierData *md;
+ ParticleSystem *psys;
if ((ob->id.tag & LIB_TAG_DOIT) == 0) {
return;
@@ -1696,11 +1708,14 @@ static void object_simplify_update(Object *ob)
ob->id.tag &= ~LIB_TAG_DOIT;
for (md = ob->modifiers.first; md; md = md->next) {
- if (ELEM(md->type, eModifierType_Subsurf, eModifierType_Multires)) {
+ if (ELEM(md->type, eModifierType_Subsurf, eModifierType_Multires, eModifierType_ParticleSystem)) {
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
}
}
+ for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ psys->recalc |= PSYS_RECALC_CHILD;
+
if (ob->dup_group) {
GroupObject *gob;
@@ -2470,6 +2485,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "uvsculpt");
RNA_def_property_ui_text(prop, "UV Sculpt", "");
+ prop = RNA_def_property(srna, "particle_edit", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "particle");
+ RNA_def_property_ui_text(prop, "Particle Edit", "");
+
prop = RNA_def_property(srna, "use_uv_sculpt", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_uv_sculpt", 1);
RNA_def_property_ui_text(prop, "UV Sculpt", "Enable brush for UV sculpting");
@@ -6521,12 +6540,22 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Simplify Subdivision", "Global maximum subdivision level");
RNA_def_property_update(prop, 0, "rna_Scene_simplify_update");
+ prop = RNA_def_property(srna, "simplify_child_particles", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "simplify_particles");
+ RNA_def_property_ui_text(prop, "Simplify Child Particles", "Global child particles percentage");
+ RNA_def_property_update(prop, 0, "rna_Scene_simplify_update");
+
prop = RNA_def_property(srna, "simplify_subdivision_render", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "simplify_subsurf_render");
RNA_def_property_ui_range(prop, 0, 6, 1, -1);
RNA_def_property_ui_text(prop, "Simplify Subdivision", "Global maximum subdivision level during rendering");
RNA_def_property_update(prop, 0, "rna_Scene_simplify_update");
+ prop = RNA_def_property(srna, "simplify_child_particles_render", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "simplify_particles_render");
+ RNA_def_property_ui_text(prop, "Simplify Child Particles", "Global child particles percentage during rendering");
+ RNA_def_property_update(prop, 0, "rna_Scene_simplify_update");
+
prop = RNA_def_property(srna, "simplify_shadow_samples", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "simplify_shadowsamples");
RNA_def_property_ui_range(prop, 1, 16, 1, -1);
@@ -7103,10 +7132,12 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_array(prop, 3);
RNA_def_property_ui_range(prop, -200.0f, 200.0f, 1, 2);
RNA_def_property_ui_text(prop, "Gravity", "Constant acceleration in a given direction");
+ RNA_def_property_update(prop, 0, "rna_Physics_update");
prop = RNA_def_property(srna, "use_gravity", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "physics_settings.flag", PHYS_GLOBAL_GRAVITY);
RNA_def_property_ui_text(prop, "Global Gravity", "Use global gravity for all dynamics");
+ RNA_def_property_update(prop, 0, "rna_Physics_update");
/* Render Data */
prop = RNA_def_property(srna, "render", PROP_POINTER, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index d1974005fce..7f405f0fb1f 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -34,7 +34,6 @@
#include "DNA_ID.h"
#include "DNA_scene_types.h"
#include "DNA_brush_types.h"
-#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -49,6 +48,18 @@
#include "BLI_utildefines.h"
#include "bmesh.h"
+static EnumPropertyItem particle_edit_hair_brush_items[] = {
+ {PE_BRUSH_NONE, "NONE", 0, "None", "Don't use any brush"},
+ {PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb hairs"},
+ {PE_BRUSH_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth hairs"},
+ {PE_BRUSH_ADD, "ADD", 0, "Add", "Add hairs"},
+ {PE_BRUSH_LENGTH, "LENGTH", 0, "Length", "Make hairs longer or shorter"},
+ {PE_BRUSH_PUFF, "PUFF", 0, "Puff", "Make hairs stand up"},
+ {PE_BRUSH_CUT, "CUT", 0, "Cut", "Cut hairs"},
+ {PE_BRUSH_WEIGHT, "WEIGHT", 0, "Weight", "Weight hair particles"},
+ {0, NULL, 0, NULL, NULL}
+};
+
EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = {
{GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points"},
{GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes"},
@@ -89,16 +100,137 @@ EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
#include "BKE_context.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_pointcache.h"
+#include "BKE_particle.h"
#include "BKE_depsgraph.h"
#include "BKE_pbvh.h"
#include "GPU_buffers.h"
+#include "ED_particle.h"
+
static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
{
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
}
+static EnumPropertyItem particle_edit_disconnected_hair_brush_items[] = {
+ {PE_BRUSH_NONE, "NONE", 0, "None", "Don't use any brush"},
+ {PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb hairs"},
+ {PE_BRUSH_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth hairs"},
+ {PE_BRUSH_LENGTH, "LENGTH", 0, "Length", "Make hairs longer or shorter"},
+ {PE_BRUSH_CUT, "CUT", 0, "Cut", "Cut hairs"},
+ {PE_BRUSH_WEIGHT, "WEIGHT", 0, "Weight", "Weight hair particles"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static EnumPropertyItem particle_edit_cache_brush_items[] = {
+ {PE_BRUSH_NONE, "NONE", 0, "None", "Don't use any brush"},
+ {PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb paths"},
+ {PE_BRUSH_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth paths"},
+ {PE_BRUSH_LENGTH, "LENGTH", 0, "Length", "Make paths longer or shorter"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static PointerRNA rna_ParticleEdit_brush_get(PointerRNA *ptr)
+{
+ ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
+ ParticleBrushData *brush = NULL;
+
+ if (pset->brushtype != PE_BRUSH_NONE)
+ brush = &pset->brush[pset->brushtype];
+
+ return rna_pointer_inherit_refine(ptr, &RNA_ParticleBrush, brush);
+}
+
+static PointerRNA rna_ParticleBrush_curve_get(PointerRNA *ptr)
+{
+ return rna_pointer_inherit_refine(ptr, &RNA_CurveMapping, NULL);
+}
+
+static void rna_ParticleEdit_redo(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+{
+ Object *ob = (scene->basact) ? scene->basact->object : NULL;
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+
+ if (!edit)
+ return;
+
+ psys_free_path_cache(edit->psys, edit);
+}
+
+static void rna_ParticleEdit_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+{
+ Object *ob = (scene->basact) ? scene->basact->object : NULL;
+
+ if (ob) DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+}
+static void rna_ParticleEdit_tool_set(PointerRNA *ptr, int value)
+{
+ ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
+
+ /* redraw hair completely if weight brush is/was used */
+ if ((pset->brushtype == PE_BRUSH_WEIGHT || value == PE_BRUSH_WEIGHT) && pset->scene) {
+ Object *ob = (pset->scene->basact) ? pset->scene->basact->object : NULL;
+ if (ob) {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+ }
+ }
+
+ pset->brushtype = value;
+}
+static EnumPropertyItem *rna_ParticleEdit_tool_itemf(bContext *C, PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = (scene->basact) ? scene->basact->object : NULL;
+#if 0
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ ParticleSystem *psys = edit ? edit->psys : NULL;
+#else
+ /* use this rather than PE_get_current() - because the editing cache is
+ * dependent on the cache being updated which can happen after this UI
+ * draws causing a glitch [#28883] */
+ ParticleSystem *psys = psys_get_current(ob);
+#endif
+
+ if (psys) {
+ if (psys->flag & PSYS_GLOBAL_HAIR) {
+ return particle_edit_disconnected_hair_brush_items;
+ }
+ else {
+ return particle_edit_hair_brush_items;
+ }
+ }
+
+ return particle_edit_cache_brush_items;
+}
+
+static int rna_ParticleEdit_editable_get(PointerRNA *ptr)
+{
+ ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
+
+ return (pset->object && pset->scene && PE_get_current(pset->scene, pset->object));
+}
+static int rna_ParticleEdit_hair_get(PointerRNA *ptr)
+{
+ ParticleEditSettings *pset = (ParticleEditSettings *)ptr->data;
+
+ if (pset->scene) {
+ PTCacheEdit *edit = PE_get_current(pset->scene, pset->object);
+
+ return (edit && edit->psys);
+ }
+
+ return 0;
+}
+
+static char *rna_ParticleEdit_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.particle_edit");
+}
+
static int rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value)
{
Scene *scene = (Scene *)ptr->id.data;
@@ -178,6 +310,11 @@ static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.uv_sculpt");
}
+static char *rna_ParticleBrush_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.particle_edit.brush");
+}
+
static void rna_Paint_brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Paint *paint = ptr->data;
@@ -680,6 +817,188 @@ static void rna_def_image_paint(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
+static void rna_def_particle_edit(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem select_mode_items[] = {
+ {SCE_SELECT_PATH, "PATH", ICON_PARTICLE_PATH, "Path", "Path edit mode"},
+ {SCE_SELECT_POINT, "POINT", ICON_PARTICLE_POINT, "Point", "Point select mode"},
+ {SCE_SELECT_END, "TIP", ICON_PARTICLE_TIP, "Tip", "Tip select mode"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem puff_mode[] = {
+ {0, "ADD", 0, "Add", "Make hairs more puffy"},
+ {1, "SUB", 0, "Sub", "Make hairs less puffy"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem length_mode[] = {
+ {0, "GROW", 0, "Grow", "Make hairs longer"},
+ {1, "SHRINK", 0, "Shrink", "Make hairs shorter"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem edit_type_items[] = {
+ {PE_TYPE_PARTICLES, "PARTICLES", 0, "Particles", ""},
+ {PE_TYPE_SOFTBODY, "SOFT_BODY", 0, "Soft body", ""},
+ {PE_TYPE_CLOTH, "CLOTH", 0, "Cloth", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+
+ /* edit */
+
+ srna = RNA_def_struct(brna, "ParticleEdit", NULL);
+ RNA_def_struct_sdna(srna, "ParticleEditSettings");
+ RNA_def_struct_path_func(srna, "rna_ParticleEdit_path");
+ RNA_def_struct_ui_text(srna, "Particle Edit", "Properties of particle editing mode");
+
+ prop = RNA_def_property(srna, "tool", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "brushtype");
+ RNA_def_property_enum_items(prop, particle_edit_hair_brush_items);
+ RNA_def_property_enum_funcs(prop, NULL, "rna_ParticleEdit_tool_set", "rna_ParticleEdit_tool_itemf");
+ RNA_def_property_ui_text(prop, "Tool", "");
+
+ prop = RNA_def_property(srna, "select_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "selectmode");
+ RNA_def_property_enum_items(prop, select_mode_items);
+ RNA_def_property_ui_text(prop, "Selection Mode", "Particle select and display mode");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_update");
+
+ prop = RNA_def_property(srna, "use_preserve_length", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_KEEP_LENGTHS);
+ RNA_def_property_ui_text(prop, "Keep Lengths", "Keep path lengths constant");
+
+ prop = RNA_def_property(srna, "use_preserve_root", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_LOCK_FIRST);
+ RNA_def_property_ui_text(prop, "Keep Root", "Keep root keys unmodified");
+
+ prop = RNA_def_property(srna, "use_emitter_deflect", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_DEFLECT_EMITTER);
+ RNA_def_property_ui_text(prop, "Deflect Emitter", "Keep paths from intersecting the emitter");
+
+ prop = RNA_def_property(srna, "emitter_distance", PROP_FLOAT, PROP_UNSIGNED);
+ RNA_def_property_float_sdna(prop, NULL, "emitterdist");
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 10, 3);
+ RNA_def_property_ui_text(prop, "Emitter Distance", "Distance to keep particles away from the emitter");
+
+ prop = RNA_def_property(srna, "use_fade_time", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_FADE_TIME);
+ RNA_def_property_ui_text(prop, "Fade Time", "Fade paths and keys further away from current frame");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_update");
+
+ prop = RNA_def_property(srna, "use_auto_velocity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_AUTO_VELOCITY);
+ RNA_def_property_ui_text(prop, "Auto Velocity", "Calculate point velocities automatically");
+
+ prop = RNA_def_property(srna, "show_particles", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_DRAW_PART);
+ RNA_def_property_ui_text(prop, "Draw Particles", "Draw actual particles");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_redo");
+
+ prop = RNA_def_property(srna, "use_default_interpolate", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_INTERPOLATE_ADDED);
+ RNA_def_property_ui_text(prop, "Interpolate", "Interpolate new particles from the existing ones");
+
+ prop = RNA_def_property(srna, "default_key_count", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "totaddkey");
+ RNA_def_property_range(prop, 2, SHRT_MAX);
+ RNA_def_property_ui_range(prop, 2, 20, 10, 3);
+ RNA_def_property_ui_text(prop, "Keys", "How many keys to make new particles with");
+
+ prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ParticleBrush");
+ RNA_def_property_pointer_funcs(prop, "rna_ParticleEdit_brush_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Brush", "");
+
+ prop = RNA_def_property(srna, "draw_step", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 1, 10);
+ RNA_def_property_ui_text(prop, "Steps", "How many steps to draw the path with");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_redo");
+
+ prop = RNA_def_property(srna, "fade_frames", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 1, 100);
+ RNA_def_property_ui_text(prop, "Frames", "How many frames to fade");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_update");
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "edittype");
+ RNA_def_property_enum_items(prop, edit_type_items);
+ RNA_def_property_ui_text(prop, "Type", "");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_redo");
+
+ prop = RNA_def_property(srna, "is_editable", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_ParticleEdit_editable_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Editable", "A valid edit mode exists");
+
+ prop = RNA_def_property(srna, "is_hair", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_ParticleEdit_hair_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Hair", "Editing hair");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "The edited object");
+
+ prop = RNA_def_property(srna, "shape_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Shape Object", "Outer shape to use for tools");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Mesh_object_poll");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_redo");
+
+ /* brush */
+
+ srna = RNA_def_struct(brna, "ParticleBrush", NULL);
+ RNA_def_struct_sdna(srna, "ParticleBrushData");
+ RNA_def_struct_path_func(srna, "rna_ParticleBrush_path");
+ RNA_def_struct_ui_text(srna, "Particle Brush", "Particle editing brush");
+
+ prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL);
+ RNA_def_property_range(prop, 1, SHRT_MAX);
+ RNA_def_property_ui_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS, 10, 3);
+ RNA_def_property_ui_text(prop, "Radius", "Radius of the brush in pixels");
+
+ prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_range(prop, 0.001, 1.0);
+ RNA_def_property_ui_text(prop, "Strength", "Brush strength");
+
+ prop = RNA_def_property(srna, "count", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 1, 1000);
+ RNA_def_property_ui_range(prop, 1, 100, 10, 3);
+ RNA_def_property_ui_text(prop, "Count", "Particle count");
+
+ prop = RNA_def_property(srna, "steps", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "step");
+ RNA_def_property_range(prop, 1, SHRT_MAX);
+ RNA_def_property_ui_range(prop, 1, 50, 10, 3);
+ RNA_def_property_ui_text(prop, "Steps", "Brush steps");
+
+ prop = RNA_def_property(srna, "puff_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "invert");
+ RNA_def_property_enum_items(prop, puff_mode);
+ RNA_def_property_ui_text(prop, "Puff Mode", "");
+
+ prop = RNA_def_property(srna, "use_puff_volume", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_BRUSH_DATA_PUFF_VOLUME);
+ RNA_def_property_ui_text(prop, "Puff Volume",
+ "Apply puff to unselected end-points (helps maintain hair volume when puffing root)");
+
+ prop = RNA_def_property(srna, "length_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "invert");
+ RNA_def_property_enum_items(prop, length_mode);
+ RNA_def_property_ui_text(prop, "Length Mode", "");
+
+ /* dummy */
+ prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_pointer_funcs(prop, "rna_ParticleBrush_curve_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Curve", "");
+}
+
static void rna_def_gpencil_sculpt(BlenderRNA *brna)
{
static EnumPropertyItem prop_direction_items[] = {
@@ -801,6 +1120,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
rna_def_uv_sculpt(brna);
rna_def_vertex_paint(brna);
rna_def_image_paint(brna);
+ rna_def_particle_edit(brna);
rna_def_gpencil_sculpt(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index 08054315334..6db370fc152 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -36,6 +36,7 @@
#include "BKE_modifier.h"
#include "BKE_smoke.h"
+#include "BKE_pointcache.h"
#include "BLI_threads.h"
@@ -52,6 +53,7 @@
#include "BKE_context.h"
#include "BKE_depsgraph.h"
+#include "BKE_particle.h"
#include "BKE_texture.h"
#include "smoke_API.h"
@@ -68,11 +70,35 @@ static void rna_Smoke_dependency_update(Main *bmain, Scene *scene, PointerRNA *p
DAG_relations_tag_update(bmain);
}
+static void rna_Smoke_resetCache(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data;
+ if (settings->smd && settings->smd->domain)
+ settings->point_cache[0]->flag |= PTCACHE_OUTDATED;
+ DAG_id_tag_update(ptr->id.data, OB_RECALC_DATA);
+}
+
+static void rna_Smoke_cachetype_set(struct PointerRNA *ptr, int value)
+{
+ SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data;
+ Object *ob = (Object *)ptr->id.data;
+
+ if (value != settings->cache_file_format) {
+ /* Clear old caches. */
+ PTCacheID id;
+ BKE_ptcache_id_from_smoke(&id, ob, settings->smd);
+ BKE_ptcache_id_clear(&id, PTCACHE_CLEAR_ALL, 0);
+
+ settings->cache_file_format = value;
+ }
+}
+
static void rna_Smoke_reset(Main *bmain, Scene *scene, PointerRNA *ptr)
{
SmokeDomainSettings *settings = (SmokeDomainSettings *)ptr->data;
smokeModifier_reset(settings->smd);
+ rna_Smoke_resetCache(bmain, scene, ptr);
rna_Smoke_update(bmain, scene, ptr);
}
@@ -83,6 +109,9 @@ static void rna_Smoke_reset_dependency(Main *bmain, Scene *scene, PointerRNA *pt
smokeModifier_reset(settings->smd);
+ if (settings->smd && settings->smd->domain)
+ settings->smd->domain->point_cache[0]->flag |= PTCACHE_OUTDATED;
+
rna_Smoke_dependency_update(bmain, scene, ptr);
}
@@ -392,6 +421,12 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
{ 0, NULL, 0, NULL, NULL }
};
+ static EnumPropertyItem smoke_cache_comp_items[] = {
+ {SM_CACHE_LIGHT, "CACHELIGHT", 0, "Light", "Fast but not so effective compression"},
+ {SM_CACHE_HEAVY, "CACHEHEAVY", 0, "Heavy", "Effective but slow compression"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
static EnumPropertyItem smoke_highres_sampling_items[] = {
{SM_HRES_FULLSAMPLE, "FULLSAMPLE", 0, "Full Sample", ""},
{SM_HRES_LINEAR, "LINEAR", 0, "Linear", ""},
@@ -413,6 +448,14 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
+ static EnumPropertyItem cache_file_type_items[] = {
+ {PTCACHE_FILE_PTCACHE, "POINTCACHE", 0, "Point Cache", "Blender specific point cache file format"},
+#ifdef WITH_OPENVDB
+ {PTCACHE_FILE_OPENVDB, "OPENVDB", 0, "OpenVDB", "OpenVDB file format"},
+#endif
+ {0, NULL, 0, NULL, NULL}
+ };
+
static EnumPropertyItem smoke_view_items[] = {
{MOD_SMOKE_SLICE_VIEW_ALIGNED, "VIEW_ALIGNED", 0, "View", "Slice volume parallel to the view plane"},
{MOD_SMOKE_SLICE_AXIS_ALIGNED, "AXIS_ALIGNED", 0, "Axis", "Slice volume parallel to the major axis"},
@@ -484,7 +527,7 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_range(prop, -5.0, 5.0, 0.02, 5);
RNA_def_property_ui_text(prop, "Density",
"How much density affects smoke motion (higher value results in faster rising smoke)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "beta", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "beta");
@@ -492,7 +535,7 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_range(prop, -5.0, 5.0, 0.02, 5);
RNA_def_property_ui_text(prop, "Heat",
"How much heat affects smoke motion (higher value results in faster rising smoke)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "coll_group");
@@ -520,24 +563,34 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0, 10.0);
RNA_def_property_ui_range(prop, 0.0, 10.0, 1, 2);
RNA_def_property_ui_text(prop, "Strength", "Strength of noise");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "dissolve_speed", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "diss_speed");
RNA_def_property_range(prop, 1.0, 10000.0);
RNA_def_property_ui_range(prop, 1.0, 10000.0, 1, -1);
RNA_def_property_ui_text(prop, "Dissolve Speed", "Dissolve Speed");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "use_dissolve_smoke", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_DISSOLVE);
RNA_def_property_ui_text(prop, "Dissolve Smoke", "Enable smoke to disappear over time");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "use_dissolve_smoke_log", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_DISSOLVE_LOG);
RNA_def_property_ui_text(prop, "Logarithmic dissolve", "Using 1/x ");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
+
+ prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "point_cache[0]");
+ RNA_def_property_ui_text(prop, "Point Cache", "");
+
+ prop = RNA_def_property(srna, "point_cache_compress_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "cache_comp");
+ RNA_def_property_enum_items(prop, smoke_cache_comp_items);
+ RNA_def_property_ui_text(prop, "Cache Compression", "Compression method to be used");
prop = RNA_def_property(srna, "openvdb_cache_compress_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "openvdb_comp");
@@ -567,21 +620,21 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "highres_sampling", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, smoke_highres_sampling_items);
RNA_def_property_ui_text(prop, "Emitter", "Method for sampling the high resolution flow");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "time_scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "time_scale");
RNA_def_property_range(prop, 0.2, 1.5);
RNA_def_property_ui_range(prop, 0.2, 1.5, 0.02, 5);
RNA_def_property_ui_text(prop, "Time Scale", "Adjust simulation speed");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "vorticity", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "vorticity");
RNA_def_property_range(prop, 0.01, 4.0);
RNA_def_property_ui_range(prop, 0.01, 4.0, 0.02, 5);
RNA_def_property_ui_text(prop, "Vorticity", "Amount of turbulence/rotation in fluid");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "density_grid", PROP_FLOAT, PROP_NONE);
RNA_def_property_array(prop, 32);
@@ -641,36 +694,36 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_range(prop, 0.01, 4.0);
RNA_def_property_ui_range(prop, 0.01, 2.0, 1.0, 5);
RNA_def_property_ui_text(prop, "Speed", "Speed of the burning reaction (use larger values for smaller flame)");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "flame_smoke", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 8.0);
RNA_def_property_ui_range(prop, 0.0, 4.0, 1.0, 5);
RNA_def_property_ui_text(prop, "Smoke", "Amount of smoke created by burning fuel");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "flame_vorticity", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 2.0);
RNA_def_property_ui_range(prop, 0.0, 1.0, 1.0, 5);
RNA_def_property_ui_text(prop, "Vorticity", "Additional vorticity for the flames");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "flame_ignition", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.5, 5.0);
RNA_def_property_ui_range(prop, 0.5, 2.5, 1.0, 5);
RNA_def_property_ui_text(prop, "Ignition", "Minimum temperature of flames");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "flame_max_temp", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 1.0, 10.0);
RNA_def_property_ui_range(prop, 1.0, 5.0, 1.0, 5);
RNA_def_property_ui_text(prop, "Maximum", "Maximum temperature of flames");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "flame_smoke_color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Smoke Color", "Color of smoke emitted from burning fuel");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "use_adaptive_domain", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_ADAPTIVE_DOMAIN);
@@ -683,20 +736,28 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_range(prop, 0, 512);
RNA_def_property_ui_range(prop, 0, 512, 2, -1);
RNA_def_property_ui_text(prop, "Additional", "Maximum number of additional cells");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "adapt_margin", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "adapt_margin");
RNA_def_property_range(prop, 2, 24);
RNA_def_property_ui_range(prop, 2, 24, 2, -1);
RNA_def_property_ui_text(prop, "Margin", "Margin added around fluid to minimize boundary interference");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
prop = RNA_def_property(srna, "adapt_threshold", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.01, 0.5);
RNA_def_property_ui_range(prop, 0.01, 0.5, 1.0, 5);
RNA_def_property_ui_text(prop, "Threshold",
"Maximum amount of fluid cell can contain before it is considered empty");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
+
+ prop = RNA_def_property(srna, "cache_file_format", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "cache_file_format");
+ RNA_def_property_enum_items(prop, cache_file_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, "rna_Smoke_cachetype_set", NULL);
+ RNA_def_property_ui_text(prop, "File Format", "Select the file format to be used for caching");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
/* display settings */
@@ -850,6 +911,13 @@ static void rna_def_smoke_flow_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Temp. Diff.", "Temperature difference to ambient temperature");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "psys");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Particle Systems", "Particle systems emitted from the object");
+ RNA_def_property_update(prop, 0, "rna_Smoke_reset_dependency");
+
prop = RNA_def_property(srna, "smoke_flow_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, smoke_flow_types);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 672322cb40d..97217f749d6 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -206,6 +206,7 @@ static EnumPropertyItem buttons_context_items[] = {
{BCONTEXT_BONE_CONSTRAINT, "BONE_CONSTRAINT", ICON_CONSTRAINT_BONE, "Bone Constraints", "Bone constraints"},
{BCONTEXT_MATERIAL, "MATERIAL", ICON_MATERIAL, "Material", "Material"},
{BCONTEXT_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture"},
+ {BCONTEXT_PARTICLE, "PARTICLES", ICON_PARTICLES, "Particles", "Particle"},
{BCONTEXT_PHYSICS, "PHYSICS", ICON_PHYSICS, "Physics", "Physics"},
{0, NULL, 0, NULL, NULL}
};
@@ -215,6 +216,7 @@ static EnumPropertyItem buttons_texture_context_items[] = {
{SB_TEXC_MATERIAL, "MATERIAL", ICON_MATERIAL, "", "Show material textures"},
{SB_TEXC_WORLD, "WORLD", ICON_WORLD, "", "Show world textures"},
{SB_TEXC_LAMP, "LAMP", ICON_LAMP, "", "Show lamp textures"},
+ {SB_TEXC_PARTICLES, "PARTICLES", ICON_PARTICLES, "", "Show particles textures"},
{SB_TEXC_LINESTYLE, "LINESTYLE", ICON_LINE_DATA, "", "Show linestyle textures"},
{SB_TEXC_OTHER, "OTHER", ICON_TEXTURE, "", "Show other data textures"},
{0, NULL, 0, NULL, NULL}
@@ -1111,6 +1113,10 @@ static EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSED(C),
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE);
}
+ if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE);
+ }
+
if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) {
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS);
}
@@ -1154,6 +1160,10 @@ static EnumPropertyItem *rna_SpaceProperties_texture_context_itemf(bContext *C,
RNA_enum_items_add_value(&item, &totitem, buttons_texture_context_items, SB_TEXC_MATERIAL);
}
+ if (ED_texture_context_check_particles(C)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_texture_context_items, SB_TEXC_PARTICLES);
+ }
+
if (ED_texture_context_check_linestyle(C)) {
RNA_enum_items_add_value(&item, &totitem, buttons_texture_context_items, SB_TEXC_LINESTYLE);
}
@@ -3741,6 +3751,11 @@ static void rna_def_space_time(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Softbody", "Show the active object's softbody point cache");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TIME, NULL);
+ prop = RNA_def_property(srna, "cache_particles", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "cache_display", TIME_CACHE_PARTICLES);
+ RNA_def_property_ui_text(prop, "Particles", "Show the active object's particle point cache");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TIME, NULL);
+
prop = RNA_def_property(srna, "cache_cloth", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "cache_display", TIME_CACHE_CLOTH);
RNA_def_property_ui_text(prop, "Cloth", "Show the active object's cloth point cache");
@@ -3879,6 +3894,8 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
{FILTER_ID_MSK, "MASK", ICON_MOD_MASK, "Masks", "Show/hide Mask data-blocks"},
{FILTER_ID_NT, "NODE_TREE", ICON_NODETREE, "Node Trees", "Show/hide Node Tree data-blocks"},
{FILTER_ID_OB, "OBJECT", ICON_OBJECT_DATA, "Objects", "Show/hide Object data-blocks"},
+ {FILTER_ID_PA, "PARTICLE_SETTINGS", ICON_PARTICLE_DATA,
+ "Particles Settings", "Show/hide Particle Settings data-blocks"},
{FILTER_ID_PAL, "PALETTE", ICON_COLOR, "Palettes", "Show/hide Palette data-blocks"},
{FILTER_ID_PC, "PAINT_CURVE", ICON_CURVE_BEZCURVE, "Paint Curves", "Show/hide Paint Curve data-blocks"},
{FILTER_ID_SCE, "SCENE", ICON_SCENE_DATA, "Scenes", "Show/hide Scene data-blocks"},
@@ -3907,7 +3924,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
"IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"},
{FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO,
"ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"},
- {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_CF,
+ {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_CF,
"MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index 959f30170f5..1e88585a286 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -35,6 +35,7 @@
#include "DNA_texture_types.h"
#include "DNA_world_types.h"
#include "DNA_node_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h" /* MAXFRAME only */
#include "BLI_utildefines.h"
@@ -252,6 +253,20 @@ void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
case ID_LS:
WM_main_add_notifier(NC_LINESTYLE, id);
break;
+ case ID_PA:
+ {
+ MTex *mtex = ptr->data;
+ int recalc = OB_RECALC_DATA;
+
+ if (mtex->mapto & PAMAP_INIT)
+ recalc |= PSYS_RECALC_RESET;
+ if (mtex->mapto & PAMAP_CHILD)
+ recalc |= PSYS_RECALC_CHILD;
+
+ DAG_id_tag_update(id, recalc);
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, NULL);
+ break;
+ }
}
}
@@ -421,6 +436,29 @@ static void rna_Envmap_update_generic(Main *bmain, Scene *scene, PointerRNA *ptr
rna_Texture_update(bmain, scene, ptr);
}
+static PointerRNA rna_PointDensity_psys_get(PointerRNA *ptr)
+{
+ PointDensity *pd = ptr->data;
+ Object *ob = pd->object;
+ ParticleSystem *psys = NULL;
+ PointerRNA value;
+
+ if (ob && pd->psys)
+ psys = BLI_findlink(&ob->particlesystem, pd->psys - 1);
+
+ RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_PointDensity_psys_set(PointerRNA *ptr, PointerRNA value)
+{
+ PointDensity *pd = ptr->data;
+ Object *ob = pd->object;
+
+ if (ob && value.id.data == ob)
+ pd->psys = BLI_findindex(&ob->particlesystem, value.data) + 1;
+}
+
static char *rna_PointDensity_path(PointerRNA *UNUSED(ptr))
{
return BLI_sprintfN("point_density");
@@ -1661,6 +1699,13 @@ static void rna_def_texture_pointdensity(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, 0, "rna_Texture_update");
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Particle System", "Particle System to render as points");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_pointer_funcs(prop, "rna_PointDensity_psys_get", "rna_PointDensity_psys_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, 0, "rna_Texture_update");
+
prop = RNA_def_property(srna, "particle_cache_space", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "psys_cache_space");
RNA_def_property_enum_items(prop, particle_cache_items);
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 0a00b966aa9..beb1d890ba9 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -3787,6 +3787,10 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_duplicate_action", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_ACT);
RNA_def_property_ui_text(prop, "Duplicate Action", "Causes actions to be duplicated with the object");
+
+ prop = RNA_def_property(srna, "use_duplicate_particle", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_PSYS);
+ RNA_def_property_ui_text(prop, "Duplicate Particle", "Causes particle systems to be duplicated with the object");
/* currently only used for insert offset (aka auto-offset), maybe also be useful for later stuff though */
prop = RNA_def_property(srna, "node_margin", PROP_INT, PROP_NONE);
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index d5b9303249a..b8ebb375a48 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -79,6 +79,8 @@ set(SRC
intern/MOD_none.c
intern/MOD_normal_edit.c
intern/MOD_ocean.c
+ intern/MOD_particleinstance.c
+ intern/MOD_particlesystem.c
intern/MOD_remesh.c
intern/MOD_screw.c
intern/MOD_shapekey.c
diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h
index 93176cb012a..4c881445893 100644
--- a/source/blender/modifiers/MOD_modifiertypes.h
+++ b/source/blender/modifiers/MOD_modifiertypes.h
@@ -50,6 +50,8 @@ extern ModifierTypeInfo modifierType_UVProject;
extern ModifierTypeInfo modifierType_Smooth;
extern ModifierTypeInfo modifierType_Cast;
extern ModifierTypeInfo modifierType_MeshDeform;
+extern ModifierTypeInfo modifierType_ParticleSystem;
+extern ModifierTypeInfo modifierType_ParticleInstance;
extern ModifierTypeInfo modifierType_Explode;
extern ModifierTypeInfo modifierType_Cloth;
extern ModifierTypeInfo modifierType_Collision;
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 8f3e1c24141..a364eef2974 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -41,10 +41,10 @@
#include "BLI_ghash.h"
#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_modifier.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#ifdef _OPENMP
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 0b99aa55c8d..d15a6fcb1c8 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -37,7 +37,6 @@
#include "DNA_cloth_types.h"
#include "DNA_key_types.h"
#include "DNA_scene_types.h"
-#include "DNA_object_force.h"
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
@@ -52,6 +51,7 @@
#include "BKE_key.h"
#include "BKE_library_query.h"
#include "BKE_modifier.h"
+#include "BKE_pointcache.h"
#include "depsgraph_private.h"
@@ -63,9 +63,10 @@ static void initData(ModifierData *md)
clmd->sim_parms = MEM_callocN(sizeof(ClothSimSettings), "cloth sim parms");
clmd->coll_parms = MEM_callocN(sizeof(ClothCollSettings), "cloth coll parms");
+ clmd->point_cache = BKE_ptcache_add(&clmd->ptcaches);
/* check for alloc failing */
- if (!clmd->sim_parms || !clmd->coll_parms)
+ if (!clmd->sim_parms || !clmd->coll_parms || !clmd->point_cache)
return;
cloth_init(clmd);
@@ -173,10 +174,15 @@ static void copyData(ModifierData *md, ModifierData *target)
if (tclmd->coll_parms)
MEM_freeN(tclmd->coll_parms);
+ BKE_ptcache_free_list(&tclmd->ptcaches);
+ tclmd->point_cache = NULL;
+
tclmd->sim_parms = MEM_dupallocN(clmd->sim_parms);
if (clmd->sim_parms->effector_weights)
tclmd->sim_parms->effector_weights = MEM_dupallocN(clmd->sim_parms->effector_weights);
tclmd->coll_parms = MEM_dupallocN(clmd->coll_parms);
+ tclmd->point_cache = BKE_ptcache_add(&tclmd->ptcaches);
+ tclmd->point_cache->step = 1;
tclmd->clothObject = NULL;
tclmd->hairdata = NULL;
tclmd->solver_result = NULL;
@@ -205,6 +211,9 @@ static void freeData(ModifierData *md)
if (clmd->coll_parms)
MEM_freeN(clmd->coll_parms);
+ BKE_ptcache_free_list(&clmd->ptcaches);
+ clmd->point_cache = NULL;
+
if (clmd->hairdata)
MEM_freeN(clmd->hairdata);
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index 8790d8083a6..e7ff0a90fbc 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -34,7 +34,6 @@
#include "DNA_object_types.h"
#include "DNA_meshdata_types.h"
-#include "DNA_object_force.h"
#include "MEM_guardedalloc.h"
@@ -46,6 +45,7 @@
#include "BKE_cdderivedmesh.h"
#include "BKE_global.h"
#include "BKE_modifier.h"
+#include "BKE_pointcache.h"
#include "BKE_scene.h"
static void initData(ModifierData *md)
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index 97f4423b798..38ffdaa709b 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -48,6 +48,7 @@
#include "BKE_lattice.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
@@ -93,10 +94,950 @@ static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
return dataMask;
}
-static DerivedMesh *applyModifier(ModifierData *UNUSED(md), Object *UNUSED(ob),
+static void createFacepa(ExplodeModifierData *emd,
+ ParticleSystemModifierData *psmd,
+ DerivedMesh *dm)
+{
+ ParticleSystem *psys = psmd->psys;
+ MFace *fa = NULL, *mface = NULL;
+ MVert *mvert = NULL;
+ ParticleData *pa;
+ KDTree *tree;
+ RNG *rng;
+ float center[3], co[3];
+ int *facepa = NULL, *vertpa = NULL, totvert = 0, totface = 0, totpart = 0;
+ int i, p, v1, v2, v3, v4 = 0;
+
+ mvert = dm->getVertArray(dm);
+ mface = dm->getTessFaceArray(dm);
+ totface = dm->getNumTessFaces(dm);
+ totvert = dm->getNumVerts(dm);
+ totpart = psmd->psys->totpart;
+
+ rng = BLI_rng_new_srandom(psys->seed);
+
+ if (emd->facepa)
+ MEM_freeN(emd->facepa);
+
+ facepa = emd->facepa = MEM_callocN(sizeof(int) * totface, "explode_facepa");
+
+ vertpa = MEM_callocN(sizeof(int) * totvert, "explode_vertpa");
+
+ /* initialize all faces & verts to no particle */
+ for (i = 0; i < totface; i++)
+ facepa[i] = totpart;
+
+ for (i = 0; i < totvert; i++)
+ vertpa[i] = totpart;
+
+ /* set protected verts */
+ if (emd->vgroup) {
+ MDeformVert *dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
+ if (dvert) {
+ const int defgrp_index = emd->vgroup - 1;
+ for (i = 0; i < totvert; i++, dvert++) {
+ float val = BLI_rng_get_float(rng);
+ val = (1.0f - emd->protect) * val + emd->protect * 0.5f;
+ if (val < defvert_find_weight(dvert, defgrp_index))
+ vertpa[i] = -1;
+ }
+ }
+ }
+
+ /* make tree of emitter locations */
+ tree = BLI_kdtree_new(totpart);
+ for (p = 0, pa = psys->particles; p < totpart; p++, pa++) {
+ psys_particle_on_emitter(psmd, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL);
+ BLI_kdtree_insert(tree, p, co);
+ }
+ BLI_kdtree_balance(tree);
+
+ /* set face-particle-indexes to nearest particle to face center */
+ for (i = 0, fa = mface; i < totface; i++, fa++) {
+ add_v3_v3v3(center, mvert[fa->v1].co, mvert[fa->v2].co);
+ add_v3_v3(center, mvert[fa->v3].co);
+ if (fa->v4) {
+ add_v3_v3(center, mvert[fa->v4].co);
+ mul_v3_fl(center, 0.25);
+ }
+ else
+ mul_v3_fl(center, 1.0f / 3.0f);
+
+ p = BLI_kdtree_find_nearest(tree, center, NULL);
+
+ v1 = vertpa[fa->v1];
+ v2 = vertpa[fa->v2];
+ v3 = vertpa[fa->v3];
+ if (fa->v4)
+ v4 = vertpa[fa->v4];
+
+ if (v1 >= 0 && v2 >= 0 && v3 >= 0 && (fa->v4 == 0 || v4 >= 0))
+ facepa[i] = p;
+
+ if (v1 >= 0) vertpa[fa->v1] = p;
+ if (v2 >= 0) vertpa[fa->v2] = p;
+ if (v3 >= 0) vertpa[fa->v3] = p;
+ if (fa->v4 && v4 >= 0) vertpa[fa->v4] = p;
+ }
+
+ if (vertpa) MEM_freeN(vertpa);
+ BLI_kdtree_free(tree);
+
+ BLI_rng_free(rng);
+}
+
+static int edgecut_get(EdgeHash *edgehash, unsigned int v1, unsigned int v2)
+{
+ return GET_INT_FROM_POINTER(BLI_edgehash_lookup(edgehash, v1, v2));
+}
+
+
+static const short add_faces[24] = {
+ 0,
+ 0, 0, 2, 0, 1, 2, 2, 0, 2, 1,
+ 2, 2, 2, 2, 3, 0, 0, 0, 1, 0,
+ 1, 1, 2
+};
+
+static MFace *get_dface(DerivedMesh *dm, DerivedMesh *split, int cur, int i, MFace *mf)
+{
+ MFace *df = CDDM_get_tessface(split, cur);
+ DM_copy_tessface_data(dm, split, i, cur, 1);
+ *df = *mf;
+ return df;
+}
+
+#define SET_VERTS(a, b, c, d) \
+ { \
+ v[0] = mf->v##a; uv[0] = a - 1; \
+ v[1] = mf->v##b; uv[1] = b - 1; \
+ v[2] = mf->v##c; uv[2] = c - 1; \
+ v[3] = mf->v##d; uv[3] = d - 1; \
+ } (void)0
+
+#define GET_ES(v1, v2) edgecut_get(eh, v1, v2)
+#define INT_UV(uvf, c0, c1) mid_v2_v2v2(uvf, mf->uv[c0], mf->uv[c1])
+
+static void remap_faces_3_6_9_12(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3, int v4)
+{
+ MFace *df1 = get_dface(dm, split, cur, i, mf);
+ MFace *df2 = get_dface(dm, split, cur + 1, i, mf);
+ MFace *df3 = get_dface(dm, split, cur + 2, i, mf);
+
+ facepa[cur] = vertpa[v1];
+ df1->v1 = v1;
+ df1->v2 = GET_ES(v1, v2);
+ df1->v3 = GET_ES(v2, v3);
+ df1->v4 = v3;
+ df1->flag |= ME_FACE_SEL;
+
+ facepa[cur + 1] = vertpa[v2];
+ df2->v1 = GET_ES(v1, v2);
+ df2->v2 = v2;
+ df2->v3 = GET_ES(v2, v3);
+ df2->v4 = 0;
+ df2->flag &= ~ME_FACE_SEL;
+
+ facepa[cur + 2] = vertpa[v1];
+ df3->v1 = v1;
+ df3->v2 = v3;
+ df3->v3 = v4;
+ df3->v4 = 0;
+ df3->flag &= ~ME_FACE_SEL;
+}
+
+static void remap_uvs_3_6_9_12(DerivedMesh *dm, DerivedMesh *split, int numlayer, int i, int cur, int c0, int c1, int c2, int c3)
+{
+ MTFace *mf, *df1, *df2, *df3;
+ int l;
+
+ for (l = 0; l < numlayer; l++) {
+ mf = CustomData_get_layer_n(&split->faceData, CD_MTFACE, l);
+ df1 = mf + cur;
+ df2 = df1 + 1;
+ df3 = df1 + 2;
+ mf = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, l);
+ mf += i;
+
+ copy_v2_v2(df1->uv[0], mf->uv[c0]);
+ INT_UV(df1->uv[1], c0, c1);
+ INT_UV(df1->uv[2], c1, c2);
+ copy_v2_v2(df1->uv[3], mf->uv[c2]);
+
+ INT_UV(df2->uv[0], c0, c1);
+ copy_v2_v2(df2->uv[1], mf->uv[c1]);
+ INT_UV(df2->uv[2], c1, c2);
+
+ copy_v2_v2(df3->uv[0], mf->uv[c0]);
+ copy_v2_v2(df3->uv[1], mf->uv[c2]);
+ copy_v2_v2(df3->uv[2], mf->uv[c3]);
+ }
+}
+
+static void remap_faces_5_10(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3, int v4)
+{
+ MFace *df1 = get_dface(dm, split, cur, i, mf);
+ MFace *df2 = get_dface(dm, split, cur + 1, i, mf);
+
+ facepa[cur] = vertpa[v1];
+ df1->v1 = v1;
+ df1->v2 = v2;
+ df1->v3 = GET_ES(v2, v3);
+ df1->v4 = GET_ES(v1, v4);
+ df1->flag |= ME_FACE_SEL;
+
+ facepa[cur + 1] = vertpa[v3];
+ df2->v1 = GET_ES(v1, v4);
+ df2->v2 = GET_ES(v2, v3);
+ df2->v3 = v3;
+ df2->v4 = v4;
+ df2->flag |= ME_FACE_SEL;
+}
+
+static void remap_uvs_5_10(DerivedMesh *dm, DerivedMesh *split, int numlayer, int i, int cur, int c0, int c1, int c2, int c3)
+{
+ MTFace *mf, *df1, *df2;
+ int l;
+
+ for (l = 0; l < numlayer; l++) {
+ mf = CustomData_get_layer_n(&split->faceData, CD_MTFACE, l);
+ df1 = mf + cur;
+ df2 = df1 + 1;
+ mf = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, l);
+ mf += i;
+
+ copy_v2_v2(df1->uv[0], mf->uv[c0]);
+ copy_v2_v2(df1->uv[1], mf->uv[c1]);
+ INT_UV(df1->uv[2], c1, c2);
+ INT_UV(df1->uv[3], c0, c3);
+
+ INT_UV(df2->uv[0], c0, c3);
+ INT_UV(df2->uv[1], c1, c2);
+ copy_v2_v2(df2->uv[2], mf->uv[c2]);
+ copy_v2_v2(df2->uv[3], mf->uv[c3]);
+
+ }
+}
+
+static void remap_faces_15(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3, int v4)
+{
+ MFace *df1 = get_dface(dm, split, cur, i, mf);
+ MFace *df2 = get_dface(dm, split, cur + 1, i, mf);
+ MFace *df3 = get_dface(dm, split, cur + 2, i, mf);
+ MFace *df4 = get_dface(dm, split, cur + 3, i, mf);
+
+ facepa[cur] = vertpa[v1];
+ df1->v1 = v1;
+ df1->v2 = GET_ES(v1, v2);
+ df1->v3 = GET_ES(v1, v3);
+ df1->v4 = GET_ES(v1, v4);
+ df1->flag |= ME_FACE_SEL;
+
+ facepa[cur + 1] = vertpa[v2];
+ df2->v1 = GET_ES(v1, v2);
+ df2->v2 = v2;
+ df2->v3 = GET_ES(v2, v3);
+ df2->v4 = GET_ES(v1, v3);
+ df2->flag |= ME_FACE_SEL;
+
+ facepa[cur + 2] = vertpa[v3];
+ df3->v1 = GET_ES(v1, v3);
+ df3->v2 = GET_ES(v2, v3);
+ df3->v3 = v3;
+ df3->v4 = GET_ES(v3, v4);
+ df3->flag |= ME_FACE_SEL;
+
+ facepa[cur + 3] = vertpa[v4];
+ df4->v1 = GET_ES(v1, v4);
+ df4->v2 = GET_ES(v1, v3);
+ df4->v3 = GET_ES(v3, v4);
+ df4->v4 = v4;
+ df4->flag |= ME_FACE_SEL;
+}
+
+static void remap_uvs_15(DerivedMesh *dm, DerivedMesh *split, int numlayer, int i, int cur, int c0, int c1, int c2, int c3)
+{
+ MTFace *mf, *df1, *df2, *df3, *df4;
+ int l;
+
+ for (l = 0; l < numlayer; l++) {
+ mf = CustomData_get_layer_n(&split->faceData, CD_MTFACE, l);
+ df1 = mf + cur;
+ df2 = df1 + 1;
+ df3 = df1 + 2;
+ df4 = df1 + 3;
+ mf = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, l);
+ mf += i;
+
+ copy_v2_v2(df1->uv[0], mf->uv[c0]);
+ INT_UV(df1->uv[1], c0, c1);
+ INT_UV(df1->uv[2], c0, c2);
+ INT_UV(df1->uv[3], c0, c3);
+
+ INT_UV(df2->uv[0], c0, c1);
+ copy_v2_v2(df2->uv[1], mf->uv[c1]);
+ INT_UV(df2->uv[2], c1, c2);
+ INT_UV(df2->uv[3], c0, c2);
+
+ INT_UV(df3->uv[0], c0, c2);
+ INT_UV(df3->uv[1], c1, c2);
+ copy_v2_v2(df3->uv[2], mf->uv[c2]);
+ INT_UV(df3->uv[3], c2, c3);
+
+ INT_UV(df4->uv[0], c0, c3);
+ INT_UV(df4->uv[1], c0, c2);
+ INT_UV(df4->uv[2], c2, c3);
+ copy_v2_v2(df4->uv[3], mf->uv[c3]);
+ }
+}
+
+static void remap_faces_7_11_13_14(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3, int v4)
+{
+ MFace *df1 = get_dface(dm, split, cur, i, mf);
+ MFace *df2 = get_dface(dm, split, cur + 1, i, mf);
+ MFace *df3 = get_dface(dm, split, cur + 2, i, mf);
+
+ facepa[cur] = vertpa[v1];
+ df1->v1 = v1;
+ df1->v2 = GET_ES(v1, v2);
+ df1->v3 = GET_ES(v2, v3);
+ df1->v4 = GET_ES(v1, v4);
+ df1->flag |= ME_FACE_SEL;
+
+ facepa[cur + 1] = vertpa[v2];
+ df2->v1 = GET_ES(v1, v2);
+ df2->v2 = v2;
+ df2->v3 = GET_ES(v2, v3);
+ df2->v4 = 0;
+ df2->flag &= ~ME_FACE_SEL;
+
+ facepa[cur + 2] = vertpa[v4];
+ df3->v1 = GET_ES(v1, v4);
+ df3->v2 = GET_ES(v2, v3);
+ df3->v3 = v3;
+ df3->v4 = v4;
+ df3->flag |= ME_FACE_SEL;
+}
+
+static void remap_uvs_7_11_13_14(DerivedMesh *dm, DerivedMesh *split, int numlayer, int i, int cur, int c0, int c1, int c2, int c3)
+{
+ MTFace *mf, *df1, *df2, *df3;
+ int l;
+
+ for (l = 0; l < numlayer; l++) {
+ mf = CustomData_get_layer_n(&split->faceData, CD_MTFACE, l);
+ df1 = mf + cur;
+ df2 = df1 + 1;
+ df3 = df1 + 2;
+ mf = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, l);
+ mf += i;
+
+ copy_v2_v2(df1->uv[0], mf->uv[c0]);
+ INT_UV(df1->uv[1], c0, c1);
+ INT_UV(df1->uv[2], c1, c2);
+ INT_UV(df1->uv[3], c0, c3);
+
+ INT_UV(df2->uv[0], c0, c1);
+ copy_v2_v2(df2->uv[1], mf->uv[c1]);
+ INT_UV(df2->uv[2], c1, c2);
+
+ INT_UV(df3->uv[0], c0, c3);
+ INT_UV(df3->uv[1], c1, c2);
+ copy_v2_v2(df3->uv[2], mf->uv[c2]);
+ copy_v2_v2(df3->uv[3], mf->uv[c3]);
+ }
+}
+
+static void remap_faces_19_21_22(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3)
+{
+ MFace *df1 = get_dface(dm, split, cur, i, mf);
+ MFace *df2 = get_dface(dm, split, cur + 1, i, mf);
+
+ facepa[cur] = vertpa[v1];
+ df1->v1 = v1;
+ df1->v2 = GET_ES(v1, v2);
+ df1->v3 = GET_ES(v1, v3);
+ df1->v4 = 0;
+ df1->flag &= ~ME_FACE_SEL;
+
+ facepa[cur + 1] = vertpa[v2];
+ df2->v1 = GET_ES(v1, v2);
+ df2->v2 = v2;
+ df2->v3 = v3;
+ df2->v4 = GET_ES(v1, v3);
+ df2->flag |= ME_FACE_SEL;
+}
+
+static void remap_uvs_19_21_22(DerivedMesh *dm, DerivedMesh *split, int numlayer, int i, int cur, int c0, int c1, int c2)
+{
+ MTFace *mf, *df1, *df2;
+ int l;
+
+ for (l = 0; l < numlayer; l++) {
+ mf = CustomData_get_layer_n(&split->faceData, CD_MTFACE, l);
+ df1 = mf + cur;
+ df2 = df1 + 1;
+ mf = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, l);
+ mf += i;
+
+ copy_v2_v2(df1->uv[0], mf->uv[c0]);
+ INT_UV(df1->uv[1], c0, c1);
+ INT_UV(df1->uv[2], c0, c2);
+
+ INT_UV(df2->uv[0], c0, c1);
+ copy_v2_v2(df2->uv[1], mf->uv[c1]);
+ copy_v2_v2(df2->uv[2], mf->uv[c2]);
+ INT_UV(df2->uv[3], c0, c2);
+ }
+}
+
+static void remap_faces_23(DerivedMesh *dm, DerivedMesh *split, MFace *mf, int *facepa, int *vertpa, int i, EdgeHash *eh, int cur, int v1, int v2, int v3)
+{
+ MFace *df1 = get_dface(dm, split, cur, i, mf);
+ MFace *df2 = get_dface(dm, split, cur + 1, i, mf);
+ MFace *df3 = get_dface(dm, split, cur + 2, i, mf);
+
+ facepa[cur] = vertpa[v1];
+ df1->v1 = v1;
+ df1->v2 = GET_ES(v1, v2);
+ df1->v3 = GET_ES(v2, v3);
+ df1->v4 = GET_ES(v1, v3);
+ df1->flag |= ME_FACE_SEL;
+
+ facepa[cur + 1] = vertpa[v2];
+ df2->v1 = GET_ES(v1, v2);
+ df2->v2 = v2;
+ df2->v3 = GET_ES(v2, v3);
+ df2->v4 = 0;
+ df2->flag &= ~ME_FACE_SEL;
+
+ facepa[cur + 2] = vertpa[v3];
+ df3->v1 = GET_ES(v1, v3);
+ df3->v2 = GET_ES(v2, v3);
+ df3->v3 = v3;
+ df3->v4 = 0;
+ df3->flag &= ~ME_FACE_SEL;
+}
+
+static void remap_uvs_23(DerivedMesh *dm, DerivedMesh *split, int numlayer, int i, int cur, int c0, int c1, int c2)
+{
+ MTFace *mf, *df1, *df2;
+ int l;
+
+ for (l = 0; l < numlayer; l++) {
+ mf = CustomData_get_layer_n(&split->faceData, CD_MTFACE, l);
+ df1 = mf + cur;
+ df2 = df1 + 1;
+ mf = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, l);
+ mf += i;
+
+ copy_v2_v2(df1->uv[0], mf->uv[c0]);
+ INT_UV(df1->uv[1], c0, c1);
+ INT_UV(df1->uv[2], c1, c2);
+ INT_UV(df1->uv[3], c0, c2);
+
+ INT_UV(df2->uv[0], c0, c1);
+ copy_v2_v2(df2->uv[1], mf->uv[c1]);
+ INT_UV(df2->uv[2], c1, c2);
+
+ INT_UV(df2->uv[0], c0, c2);
+ INT_UV(df2->uv[1], c1, c2);
+ copy_v2_v2(df2->uv[2], mf->uv[c2]);
+ }
+}
+
+static DerivedMesh *cutEdges(ExplodeModifierData *emd, DerivedMesh *dm)
+{
+ DerivedMesh *splitdm;
+ MFace *mf = NULL, *df1 = NULL;
+ MFace *mface = dm->getTessFaceArray(dm);
+ MVert *dupve, *mv;
+ EdgeHash *edgehash;
+ EdgeHashIterator *ehi;
+ int totvert = dm->getNumVerts(dm);
+ int totface = dm->getNumTessFaces(dm);
+
+ int *facesplit = MEM_callocN(sizeof(int) * totface, "explode_facesplit");
+ int *vertpa = MEM_callocN(sizeof(int) * totvert, "explode_vertpa2");
+ int *facepa = emd->facepa;
+ int *fs, totesplit = 0, totfsplit = 0, curdupface = 0;
+ int i, v1, v2, v3, v4, esplit,
+ v[4] = {0, 0, 0, 0}, /* To quite gcc barking... */
+ uv[4] = {0, 0, 0, 0}; /* To quite gcc barking... */
+ int numlayer;
+ unsigned int ed_v1, ed_v2;
+
+ edgehash = BLI_edgehash_new(__func__);
+
+ /* recreate vertpa from facepa calculation */
+ for (i = 0, mf = mface; i < totface; i++, mf++) {
+ vertpa[mf->v1] = facepa[i];
+ vertpa[mf->v2] = facepa[i];
+ vertpa[mf->v3] = facepa[i];
+ if (mf->v4)
+ vertpa[mf->v4] = facepa[i];
+ }
+
+ /* mark edges for splitting and how to split faces */
+ for (i = 0, mf = mface, fs = facesplit; i < totface; i++, mf++, fs++) {
+ v1 = vertpa[mf->v1];
+ v2 = vertpa[mf->v2];
+ v3 = vertpa[mf->v3];
+
+ if (v1 != v2) {
+ BLI_edgehash_reinsert(edgehash, mf->v1, mf->v2, NULL);
+ (*fs) |= 1;
+ }
+
+ if (v2 != v3) {
+ BLI_edgehash_reinsert(edgehash, mf->v2, mf->v3, NULL);
+ (*fs) |= 2;
+ }
+
+ if (mf->v4) {
+ v4 = vertpa[mf->v4];
+
+ if (v3 != v4) {
+ BLI_edgehash_reinsert(edgehash, mf->v3, mf->v4, NULL);
+ (*fs) |= 4;
+ }
+
+ if (v1 != v4) {
+ BLI_edgehash_reinsert(edgehash, mf->v1, mf->v4, NULL);
+ (*fs) |= 8;
+ }
+
+ /* mark center vertex as a fake edge split */
+ if (*fs == 15)
+ BLI_edgehash_reinsert(edgehash, mf->v1, mf->v3, NULL);
+ }
+ else {
+ (*fs) |= 16; /* mark face as tri */
+
+ if (v1 != v3) {
+ BLI_edgehash_reinsert(edgehash, mf->v1, mf->v3, NULL);
+ (*fs) |= 4;
+ }
+ }
+ }
+
+ /* count splits & create indexes for new verts */
+ ehi = BLI_edgehashIterator_new(edgehash);
+ totesplit = totvert;
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ BLI_edgehashIterator_setValue(ehi, SET_INT_IN_POINTER(totesplit));
+ totesplit++;
+ }
+ BLI_edgehashIterator_free(ehi);
+
+ /* count new faces due to splitting */
+ for (i = 0, fs = facesplit; i < totface; i++, fs++)
+ totfsplit += add_faces[*fs];
+
+ splitdm = CDDM_from_template_ex(
+ dm, totesplit, 0, totface + totfsplit, 0, 0,
+ CD_MASK_DERIVEDMESH | CD_MASK_FACECORNERS);
+ numlayer = CustomData_number_of_layers(&splitdm->faceData, CD_MTFACE);
+
+ /* copy new faces & verts (is it really this painful with custom data??) */
+ for (i = 0; i < totvert; i++) {
+ MVert source;
+ MVert *dest;
+ dm->getVert(dm, i, &source);
+ dest = CDDM_get_vert(splitdm, i);
+
+ DM_copy_vert_data(dm, splitdm, i, i, 1);
+ *dest = source;
+ }
+
+ /* override original facepa (original pointer is saved in caller function) */
+
+ /* BMESH_TODO, (totfsplit * 2) over allocation is used since the quads are
+ * later interpreted as tri's, for this to work right I think we probably
+ * have to stop using tessface - campbell */
+
+ facepa = MEM_callocN(sizeof(int) * (totface + (totfsplit * 2)), "explode_facepa");
+ //memcpy(facepa, emd->facepa, totface*sizeof(int));
+ emd->facepa = facepa;
+
+ /* create new verts */
+ ehi = BLI_edgehashIterator_new(edgehash);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ BLI_edgehashIterator_getKey(ehi, &ed_v1, &ed_v2);
+ esplit = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
+ mv = CDDM_get_vert(splitdm, ed_v2);
+ dupve = CDDM_get_vert(splitdm, esplit);
+
+ DM_copy_vert_data(splitdm, splitdm, ed_v2, esplit, 1);
+
+ *dupve = *mv;
+
+ mv = CDDM_get_vert(splitdm, ed_v1);
+
+ mid_v3_v3v3(dupve->co, dupve->co, mv->co);
+ }
+ BLI_edgehashIterator_free(ehi);
+
+ /* create new faces */
+ curdupface = 0; //=totface;
+ //curdupin=totesplit;
+ for (i = 0, fs = facesplit; i < totface; i++, fs++) {
+ mf = dm->getTessFaceData(dm, i, CD_MFACE);
+
+ switch (*fs) {
+ case 3:
+ case 10:
+ case 11:
+ case 15:
+ SET_VERTS(1, 2, 3, 4);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ SET_VERTS(2, 3, 4, 1);
+ break;
+ case 9:
+ case 13:
+ SET_VERTS(4, 1, 2, 3);
+ break;
+ case 12:
+ case 14:
+ SET_VERTS(3, 4, 1, 2);
+ break;
+ case 21:
+ case 23:
+ SET_VERTS(1, 2, 3, 4);
+ break;
+ case 19:
+ SET_VERTS(2, 3, 1, 4);
+ break;
+ case 22:
+ SET_VERTS(3, 1, 2, 4);
+ break;
+ }
+
+ switch (*fs) {
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ remap_faces_3_6_9_12(dm, splitdm, mf, facepa, vertpa, i, edgehash, curdupface, v[0], v[1], v[2], v[3]);
+ if (numlayer)
+ remap_uvs_3_6_9_12(dm, splitdm, numlayer, i, curdupface, uv[0], uv[1], uv[2], uv[3]);
+ break;
+ case 5:
+ case 10:
+ remap_faces_5_10(dm, splitdm, mf, facepa, vertpa, i, edgehash, curdupface, v[0], v[1], v[2], v[3]);
+ if (numlayer)
+ remap_uvs_5_10(dm, splitdm, numlayer, i, curdupface, uv[0], uv[1], uv[2], uv[3]);
+ break;
+ case 15:
+ remap_faces_15(dm, splitdm, mf, facepa, vertpa, i, edgehash, curdupface, v[0], v[1], v[2], v[3]);
+ if (numlayer)
+ remap_uvs_15(dm, splitdm, numlayer, i, curdupface, uv[0], uv[1], uv[2], uv[3]);
+ break;
+ case 7:
+ case 11:
+ case 13:
+ case 14:
+ remap_faces_7_11_13_14(dm, splitdm, mf, facepa, vertpa, i, edgehash, curdupface, v[0], v[1], v[2], v[3]);
+ if (numlayer)
+ remap_uvs_7_11_13_14(dm, splitdm, numlayer, i, curdupface, uv[0], uv[1], uv[2], uv[3]);
+ break;
+ case 19:
+ case 21:
+ case 22:
+ remap_faces_19_21_22(dm, splitdm, mf, facepa, vertpa, i, edgehash, curdupface, v[0], v[1], v[2]);
+ if (numlayer)
+ remap_uvs_19_21_22(dm, splitdm, numlayer, i, curdupface, uv[0], uv[1], uv[2]);
+ break;
+ case 23:
+ remap_faces_23(dm, splitdm, mf, facepa, vertpa, i, edgehash, curdupface, v[0], v[1], v[2]);
+ if (numlayer)
+ remap_uvs_23(dm, splitdm, numlayer, i, curdupface, uv[0], uv[1], uv[2]);
+ break;
+ case 0:
+ case 16:
+ df1 = get_dface(dm, splitdm, curdupface, i, mf);
+ facepa[curdupface] = vertpa[mf->v1];
+
+ if (df1->v4)
+ df1->flag |= ME_FACE_SEL;
+ else
+ df1->flag &= ~ME_FACE_SEL;
+ break;
+ }
+
+ curdupface += add_faces[*fs] + 1;
+ }
+
+ for (i = 0; i < curdupface; i++) {
+ mf = CDDM_get_tessface(splitdm, i);
+ test_index_face(mf, &splitdm->faceData, i, ((mf->flag & ME_FACE_SEL) ? 4 : 3));
+ }
+
+ BLI_edgehash_free(edgehash, NULL);
+ MEM_freeN(facesplit);
+ MEM_freeN(vertpa);
+
+ CDDM_calc_edges_tessface(splitdm);
+ CDDM_tessfaces_to_faces(splitdm); /*builds ngon faces from tess (mface) faces*/
+
+ return splitdm;
+}
+static DerivedMesh *explodeMesh(ExplodeModifierData *emd,
+ ParticleSystemModifierData *psmd, Scene *scene, Object *ob,
+ DerivedMesh *to_explode)
+{
+ DerivedMesh *explode, *dm = to_explode;
+ MFace *mf = NULL, *mface;
+ /* ParticleSettings *part=psmd->psys->part; */ /* UNUSED */
+ ParticleSimulationData sim = {NULL};
+ ParticleData *pa = NULL, *pars = psmd->psys->particles;
+ ParticleKey state, birth;
+ EdgeHash *vertpahash;
+ EdgeHashIterator *ehi;
+ float *vertco = NULL, imat[4][4];
+ float rot[4];
+ float cfra;
+ /* float timestep; */
+ const int *facepa = emd->facepa;
+ int totdup = 0, totvert = 0, totface = 0, totpart = 0, delface = 0;
+ int i, v, u;
+ unsigned int ed_v1, ed_v2, mindex = 0;
+ MTFace *mtface = NULL, *mtf;
+
+ totface = dm->getNumTessFaces(dm);
+ totvert = dm->getNumVerts(dm);
+ mface = dm->getTessFaceArray(dm);
+ totpart = psmd->psys->totpart;
+
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psmd->psys;
+ sim.psmd = psmd;
+
+ /* timestep = psys_get_timestep(&sim); */
+
+ cfra = BKE_scene_frame_get(scene);
+
+ /* hash table for vertice <-> particle relations */
+ vertpahash = BLI_edgehash_new(__func__);
+
+ for (i = 0; i < totface; i++) {
+ if (facepa[i] != totpart) {
+ pa = pars + facepa[i];
+
+ if ((pa->alive == PARS_UNBORN && (emd->flag & eExplodeFlag_Unborn) == 0) ||
+ (pa->alive == PARS_ALIVE && (emd->flag & eExplodeFlag_Alive) == 0) ||
+ (pa->alive == PARS_DEAD && (emd->flag & eExplodeFlag_Dead) == 0))
+ {
+ delface++;
+ continue;
+ }
+ }
+
+ /* do mindex + totvert to ensure the vertex index to be the first
+ * with BLI_edgehashIterator_getKey */
+ if (facepa[i] == totpart || cfra < (pars + facepa[i])->time)
+ mindex = totvert + totpart;
+ else
+ mindex = totvert + facepa[i];
+
+ mf = &mface[i];
+
+ /* set face vertices to exist in particle group */
+ BLI_edgehash_reinsert(vertpahash, mf->v1, mindex, NULL);
+ BLI_edgehash_reinsert(vertpahash, mf->v2, mindex, NULL);
+ BLI_edgehash_reinsert(vertpahash, mf->v3, mindex, NULL);
+ if (mf->v4)
+ BLI_edgehash_reinsert(vertpahash, mf->v4, mindex, NULL);
+ }
+
+ /* make new vertice indexes & count total vertices after duplication */
+ ehi = BLI_edgehashIterator_new(vertpahash);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ BLI_edgehashIterator_setValue(ehi, SET_INT_IN_POINTER(totdup));
+ totdup++;
+ }
+ BLI_edgehashIterator_free(ehi);
+
+ /* the final duplicated vertices */
+ explode = CDDM_from_template_ex(dm, totdup, 0, totface - delface, 0, 0, CD_MASK_DERIVEDMESH | CD_MASK_FACECORNERS);
+ mtface = CustomData_get_layer_named(&explode->faceData, CD_MTFACE, emd->uvname);
+ /*dupvert = CDDM_get_verts(explode);*/
+
+ /* getting back to object space */
+ invert_m4_m4(imat, ob->obmat);
+
+ psmd->psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ /* duplicate & displace vertices */
+ ehi = BLI_edgehashIterator_new(vertpahash);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ MVert source;
+ MVert *dest;
+
+ /* get particle + vertex from hash */
+ BLI_edgehashIterator_getKey(ehi, &ed_v1, &ed_v2);
+ ed_v2 -= totvert;
+ v = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
+
+ dm->getVert(dm, ed_v1, &source);
+ dest = CDDM_get_vert(explode, v);
+
+ DM_copy_vert_data(dm, explode, ed_v1, v, 1);
+ *dest = source;
+
+ if (ed_v2 != totpart) {
+ /* get particle */
+ pa = pars + ed_v2;
+
+ psys_get_birth_coords(&sim, pa, &birth, 0, 0);
+
+ state.time = cfra;
+ psys_get_particle_state(&sim, ed_v2, &state, 1);
+
+ vertco = CDDM_get_vert(explode, v)->co;
+ mul_m4_v3(ob->obmat, vertco);
+
+ sub_v3_v3(vertco, birth.co);
+
+ /* apply rotation, size & location */
+ sub_qt_qtqt(rot, state.rot, birth.rot);
+ mul_qt_v3(rot, vertco);
+
+ if (emd->flag & eExplodeFlag_PaSize)
+ mul_v3_fl(vertco, pa->size);
+
+ add_v3_v3(vertco, state.co);
+
+ mul_m4_v3(imat, vertco);
+ }
+ }
+ BLI_edgehashIterator_free(ehi);
+
+ /*map new vertices to faces*/
+ for (i = 0, u = 0; i < totface; i++) {
+ MFace source;
+ int orig_v4;
+
+ if (facepa[i] != totpart) {
+ pa = pars + facepa[i];
+
+ if (pa->alive == PARS_UNBORN && (emd->flag & eExplodeFlag_Unborn) == 0) continue;
+ if (pa->alive == PARS_ALIVE && (emd->flag & eExplodeFlag_Alive) == 0) continue;
+ if (pa->alive == PARS_DEAD && (emd->flag & eExplodeFlag_Dead) == 0) continue;
+ }
+
+ dm->getTessFace(dm, i, &source);
+ mf = CDDM_get_tessface(explode, u);
+
+ orig_v4 = source.v4;
+
+ if (facepa[i] != totpart && cfra < pa->time)
+ mindex = totvert + totpart;
+ else
+ mindex = totvert + facepa[i];
+
+ source.v1 = edgecut_get(vertpahash, source.v1, mindex);
+ source.v2 = edgecut_get(vertpahash, source.v2, mindex);
+ source.v3 = edgecut_get(vertpahash, source.v3, mindex);
+ if (source.v4)
+ source.v4 = edgecut_get(vertpahash, source.v4, mindex);
+
+ DM_copy_tessface_data(dm, explode, i, u, 1);
+
+ *mf = source;
+
+ /* override uv channel for particle age */
+ if (mtface) {
+ float age = (cfra - pa->time) / pa->lifetime;
+ /* Clamp to this range to avoid flipping to the other side of the coordinates. */
+ CLAMP(age, 0.001f, 0.999f);
+
+ mtf = mtface + u;
+
+ mtf->uv[0][0] = mtf->uv[1][0] = mtf->uv[2][0] = mtf->uv[3][0] = age;
+ mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = 0.5f;
+ }
+
+ test_index_face(mf, &explode->faceData, u, (orig_v4 ? 4 : 3));
+ u++;
+ }
+
+ /* cleanup */
+ BLI_edgehash_free(vertpahash, NULL);
+
+ /* finalization */
+ CDDM_calc_edges_tessface(explode);
+ CDDM_tessfaces_to_faces(explode);
+ explode->dirty |= DM_DIRTY_NORMALS;
+
+ if (psmd->psys->lattice_deform_data) {
+ end_latt_deform(psmd->psys->lattice_deform_data);
+ psmd->psys->lattice_deform_data = NULL;
+ }
+
+ return explode;
+}
+
+static ParticleSystemModifierData *findPrecedingParticlesystem(Object *ob, ModifierData *emd)
+{
+ ModifierData *md;
+ ParticleSystemModifierData *psmd = NULL;
+
+ for (md = ob->modifiers.first; emd != md; md = md->next) {
+ if (md->type == eModifierType_ParticleSystem)
+ psmd = (ParticleSystemModifierData *) md;
+ }
+ return psmd;
+}
+static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
DerivedMesh *derivedData,
ModifierApplyFlag UNUSED(flag))
{
+ DerivedMesh *dm = derivedData;
+ ExplodeModifierData *emd = (ExplodeModifierData *) md;
+ ParticleSystemModifierData *psmd = findPrecedingParticlesystem(ob, md);
+
+ DM_ensure_tessface(dm); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */
+
+ if (psmd) {
+ ParticleSystem *psys = psmd->psys;
+
+ if (psys == NULL || psys->totpart == 0) return derivedData;
+ if (psys->part == NULL || psys->particles == NULL) return derivedData;
+ if (psmd->dm_final == NULL) return derivedData;
+
+ /* 1. find faces to be exploded if needed */
+ if (emd->facepa == NULL ||
+ psmd->flag & eParticleSystemFlag_Pars ||
+ emd->flag & eExplodeFlag_CalcFaces ||
+ MEM_allocN_len(emd->facepa) / sizeof(int) != dm->getNumTessFaces(dm))
+ {
+ if (psmd->flag & eParticleSystemFlag_Pars)
+ psmd->flag &= ~eParticleSystemFlag_Pars;
+
+ if (emd->flag & eExplodeFlag_CalcFaces)
+ emd->flag &= ~eExplodeFlag_CalcFaces;
+
+ createFacepa(emd, psmd, derivedData);
+ }
+ /* 2. create new mesh */
+ if (emd->flag & eExplodeFlag_EdgeCut) {
+ int *facepa = emd->facepa;
+ DerivedMesh *splitdm = cutEdges(emd, dm);
+ DerivedMesh *explode = explodeMesh(emd, psmd, md->scene, ob, splitdm);
+
+ MEM_freeN(emd->facepa);
+ emd->facepa = facepa;
+ splitdm->release(splitdm);
+ return explode;
+ }
+ else
+ return explodeMesh(emd, psmd, md->scene, ob, derivedData);
+ }
return derivedData;
}
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index 5439f77aede..ce3fdc4bbe8 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -33,12 +33,11 @@
#include "BLI_math.h"
#include "BLI_string.h"
-#include "DNA_object_types.h"
-
#include "MEM_guardedalloc.h"
#include "BKE_mesh_mapping.h"
#include "BKE_cdderivedmesh.h"
+#include "BKE_particle.h"
#include "BKE_deform.h"
#include "MOD_util.h"
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
new file mode 100644
index 00000000000..4e78e758dc3
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -0,0 +1,470 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 by the Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Daniel Dunbar
+ * Ton Roosendaal,
+ * Ben Batt,
+ * Brecht Van Lommel,
+ * Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file blender/modifiers/intern/MOD_particleinstance.c
+ * \ingroup modifiers
+ */
+
+
+#include "DNA_meshdata_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_listbase.h"
+#include "BLI_rand.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_effect.h"
+#include "BKE_global.h"
+#include "BKE_lattice.h"
+#include "BKE_library_query.h"
+#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+
+#include "depsgraph_private.h"
+#include "DEG_depsgraph_build.h"
+
+static void initData(ModifierData *md)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md;
+
+ pimd->flag = eParticleInstanceFlag_Parents | eParticleInstanceFlag_Unborn |
+ eParticleInstanceFlag_Alive | eParticleInstanceFlag_Dead;
+ pimd->psys = 1;
+ pimd->position = 1.0f;
+ pimd->axis = 2;
+
+}
+static void copyData(ModifierData *md, ModifierData *target)
+{
+#if 0
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md;
+ ParticleInstanceModifierData *tpimd = (ParticleInstanceModifierData *) target;
+#endif
+ modifier_copyData_generic(md, target);
+}
+
+static bool isDisabled(ModifierData *md, int useRenderParams)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md;
+ ParticleSystem *psys;
+ ModifierData *ob_md;
+
+ if (!pimd->ob)
+ return true;
+
+ psys = BLI_findlink(&pimd->ob->particlesystem, pimd->psys - 1);
+ if (psys == NULL)
+ return true;
+
+ /* If the psys modifier is disabled we cannot use its data.
+ * First look up the psys modifier from the object, then check if it is enabled.
+ */
+ for (ob_md = pimd->ob->modifiers.first; ob_md; ob_md = ob_md->next) {
+ if (ob_md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)ob_md;
+ if (psmd->psys == psys) {
+ int required_mode;
+
+ if (useRenderParams) required_mode = eModifierMode_Render;
+ else required_mode = eModifierMode_Realtime;
+
+ if (!modifier_isEnabled(md->scene, ob_md, required_mode))
+ return true;
+
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+static void updateDepgraph(ModifierData *md, DagForest *forest,
+ struct Main *UNUSED(bmain),
+ struct Scene *UNUSED(scene),
+ Object *UNUSED(ob),
+ DagNode *obNode)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md;
+
+ if (pimd->ob) {
+ DagNode *curNode = dag_get_node(forest, pimd->ob);
+
+ dag_add_relation(forest, curNode, obNode,
+ DAG_RL_DATA_DATA | DAG_RL_OB_DATA,
+ "Particle Instance Modifier");
+ }
+}
+
+static void updateDepsgraph(ModifierData *md,
+ struct Main *UNUSED(bmain),
+ struct Scene *UNUSED(scene),
+ Object *UNUSED(ob),
+ struct DepsNodeHandle *node)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md;
+ if (pimd->ob != NULL) {
+ DEG_add_object_relation(node, pimd->ob, DEG_OB_COMP_TRANSFORM, "Particle Instance Modifier");
+ }
+}
+
+static void foreachObjectLink(ModifierData *md, Object *ob,
+ ObjectWalkFunc walk, void *userData)
+{
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md;
+
+ walk(userData, ob, &pimd->ob, IDWALK_NOP);
+}
+
+static int particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psys, int p)
+{
+ ParticleData *pa;
+
+ if (pimd->flag & eParticleInstanceFlag_Parents) {
+ if (p >= psys->totpart) {
+ if (psys->part->childtype == PART_CHILD_PARTICLES) {
+ pa = psys->particles + (psys->child + p - psys->totpart)->parent;
+ }
+ else {
+ pa = NULL;
+ }
+ }
+ else {
+ pa = psys->particles + p;
+ }
+ }
+ else {
+ if (psys->part->childtype == PART_CHILD_PARTICLES) {
+ pa = psys->particles + (psys->child + p)->parent;
+ }
+ else {
+ pa = NULL;
+ }
+ }
+
+ if (pa) {
+ if (pa->alive == PARS_UNBORN && (pimd->flag & eParticleInstanceFlag_Unborn) == 0) return 1;
+ if (pa->alive == PARS_ALIVE && (pimd->flag & eParticleInstanceFlag_Alive) == 0) return 1;
+ if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) return 1;
+ }
+
+ return 0;
+}
+
+static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
+ DerivedMesh *derivedData,
+ ModifierApplyFlag UNUSED(flag))
+{
+ DerivedMesh *dm = derivedData, *result;
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md;
+ ParticleSimulationData sim;
+ ParticleSystem *psys = NULL;
+ ParticleData *pa = NULL;
+ MPoly *mpoly, *orig_mpoly;
+ MLoop *mloop, *orig_mloop;
+ MVert *mvert, *orig_mvert;
+ int totvert, totpoly, totloop /* , totedge */;
+ int maxvert, maxpoly, maxloop, totpart = 0, first_particle = 0;
+ int k, p, p_skip;
+ short track = ob->trackflag % 3, trackneg, axis = pimd->axis;
+ float max_co = 0.0, min_co = 0.0, temp_co[3];
+ float *size = NULL;
+
+ trackneg = ((ob->trackflag > 2) ? 1 : 0);
+
+ if (pimd->ob == ob) {
+ pimd->ob = NULL;
+ return derivedData;
+ }
+
+ if (pimd->ob) {
+ psys = BLI_findlink(&pimd->ob->particlesystem, pimd->psys - 1);
+ if (psys == NULL || psys->totpart == 0)
+ return derivedData;
+ }
+ else {
+ return derivedData;
+ }
+
+ if (pimd->flag & eParticleInstanceFlag_Parents)
+ totpart += psys->totpart;
+ if (pimd->flag & eParticleInstanceFlag_Children) {
+ if (totpart == 0)
+ first_particle = psys->totpart;
+ totpart += psys->totchild;
+ }
+
+ if (totpart == 0)
+ return derivedData;
+
+ sim.scene = md->scene;
+ sim.ob = pimd->ob;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(pimd->ob, psys);
+
+ if (pimd->flag & eParticleInstanceFlag_UseSize) {
+ float *si;
+ si = size = MEM_callocN(totpart * sizeof(float), "particle size array");
+
+ if (pimd->flag & eParticleInstanceFlag_Parents) {
+ for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++, si++)
+ *si = pa->size;
+ }
+
+ if (pimd->flag & eParticleInstanceFlag_Children) {
+ ChildParticle *cpa = psys->child;
+
+ for (p = 0; p < psys->totchild; p++, cpa++, si++) {
+ *si = psys_get_child_size(psys, cpa, 0.0f, NULL);
+ }
+ }
+ }
+
+ totvert = dm->getNumVerts(dm);
+ totpoly = dm->getNumPolys(dm);
+ totloop = dm->getNumLoops(dm);
+ /* totedge = dm->getNumEdges(dm); */ /* UNUSED */
+
+ /* count particles */
+ maxvert = 0;
+ maxpoly = 0;
+ maxloop = 0;
+
+ for (p = 0; p < totpart; p++) {
+ if (particle_skip(pimd, psys, p))
+ continue;
+
+ maxvert += totvert;
+ maxpoly += totpoly;
+ maxloop += totloop;
+ }
+
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ if (psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) {
+ float min[3], max[3];
+ INIT_MINMAX(min, max);
+ dm->getMinMax(dm, min, max);
+ min_co = min[track];
+ max_co = max[track];
+ }
+
+ result = CDDM_from_template(dm, maxvert, 0, 0, maxloop, maxpoly);
+
+ mvert = result->getVertArray(result);
+ orig_mvert = dm->getVertArray(dm);
+
+ mpoly = result->getPolyArray(result);
+ orig_mpoly = dm->getPolyArray(dm);
+ mloop = result->getLoopArray(result);
+ orig_mloop = dm->getLoopArray(dm);
+
+ for (p = 0, p_skip = 0; p < totpart; p++) {
+ float prev_dir[3];
+ float frame[4]; /* frame orientation quaternion */
+
+ /* skip particle? */
+ if (particle_skip(pimd, psys, p))
+ continue;
+
+ /* set vertices coordinates */
+ for (k = 0; k < totvert; k++) {
+ ParticleKey state;
+ MVert *inMV;
+ MVert *mv = mvert + p_skip * totvert + k;
+
+ inMV = orig_mvert + k;
+ DM_copy_vert_data(dm, result, k, p_skip * totvert + k, 1);
+ *mv = *inMV;
+
+ /*change orientation based on object trackflag*/
+ copy_v3_v3(temp_co, mv->co);
+ mv->co[axis] = temp_co[track];
+ mv->co[(axis + 1) % 3] = temp_co[(track + 1) % 3];
+ mv->co[(axis + 2) % 3] = temp_co[(track + 2) % 3];
+
+ /* get particle state */
+ if ((psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) &&
+ (pimd->flag & eParticleInstanceFlag_Path))
+ {
+ float ran = 0.0f;
+ if (pimd->random_position != 0.0f) {
+ ran = pimd->random_position * BLI_hash_frand(psys->seed + p);
+ }
+
+ if (pimd->flag & eParticleInstanceFlag_KeepShape) {
+ state.time = pimd->position * (1.0f - ran);
+ }
+ else {
+ state.time = (mv->co[axis] - min_co) / (max_co - min_co) * pimd->position * (1.0f - ran);
+
+ if (trackneg)
+ state.time = 1.0f - state.time;
+
+ mv->co[axis] = 0.0;
+ }
+
+ psys_get_particle_on_path(&sim, first_particle + p, &state, 1);
+
+ normalize_v3(state.vel);
+
+ /* Incrementally Rotating Frame (Bishop Frame) */
+ if (k == 0) {
+ float hairmat[4][4];
+ float mat[3][3];
+
+ if (first_particle + p < psys->totpart)
+ pa = psys->particles + first_particle + p;
+ else {
+ ChildParticle *cpa = psys->child + (p - psys->totpart);
+ pa = psys->particles + cpa->parent;
+ }
+ psys_mat_hair_to_global(sim.ob, sim.psmd->dm_final, sim.psys->part->from, pa, hairmat);
+ copy_m3_m4(mat, hairmat);
+ /* to quaternion */
+ mat3_to_quat(frame, mat);
+
+ /* note: direction is same as normal vector currently,
+ * but best to keep this separate so the frame can be
+ * rotated later if necessary
+ */
+ copy_v3_v3(prev_dir, state.vel);
+ }
+ else {
+ float rot[4];
+
+ /* incrementally rotate along bend direction */
+ rotation_between_vecs_to_quat(rot, prev_dir, state.vel);
+ mul_qt_qtqt(frame, rot, frame);
+
+ copy_v3_v3(prev_dir, state.vel);
+ }
+
+ copy_qt_qt(state.rot, frame);
+#if 0
+ /* Absolute Frame (Frenet Frame) */
+ if (state.vel[axis] < -0.9999f || state.vel[axis] > 0.9999f) {
+ unit_qt(state.rot);
+ }
+ else {
+ float cross[3];
+ float temp[3] = {0.0f, 0.0f, 0.0f};
+ temp[axis] = 1.0f;
+
+ cross_v3_v3v3(cross, temp, state.vel);
+
+ /* state.vel[axis] is the only component surviving from a dot product with the axis */
+ axis_angle_to_quat(state.rot, cross, saacos(state.vel[axis]));
+ }
+#endif
+ }
+ else {
+ state.time = -1.0;
+ psys_get_particle_state(&sim, first_particle + p, &state, 1);
+ }
+
+ mul_qt_v3(state.rot, mv->co);
+ if (pimd->flag & eParticleInstanceFlag_UseSize)
+ mul_v3_fl(mv->co, size[p]);
+ add_v3_v3(mv->co, state.co);
+ }
+
+ /* create polys and loops */
+ for (k = 0; k < totpoly; k++) {
+ MPoly *inMP = orig_mpoly + k;
+ MPoly *mp = mpoly + p_skip * totpoly + k;
+
+ DM_copy_poly_data(dm, result, k, p_skip * totpoly + k, 1);
+ *mp = *inMP;
+ mp->loopstart += p_skip * totloop;
+
+ {
+ MLoop *inML = orig_mloop + inMP->loopstart;
+ MLoop *ml = mloop + mp->loopstart;
+ int j = mp->totloop;
+
+ DM_copy_loop_data(dm, result, inMP->loopstart, mp->loopstart, j);
+ for (; j; j--, ml++, inML++) {
+ ml->v = inML->v + (p_skip * totvert);
+ }
+ }
+ }
+
+ p_skip++;
+ }
+
+ CDDM_calc_edges(result);
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (size)
+ MEM_freeN(size);
+
+ result->dirty |= DM_DIRTY_NORMALS;
+
+ return result;
+}
+ModifierTypeInfo modifierType_ParticleInstance = {
+ /* name */ "ParticleInstance",
+ /* structName */ "ParticleInstanceModifierData",
+ /* structSize */ sizeof(ParticleInstanceModifierData),
+ /* type */ eModifierTypeType_Constructive,
+ /* flags */ eModifierTypeFlag_AcceptsMesh |
+ eModifierTypeFlag_SupportsMapping |
+ eModifierTypeFlag_SupportsEditmode |
+ eModifierTypeFlag_EnableInEditmode,
+
+ /* copyData */ copyData,
+ /* deformVerts */ NULL,
+ /* deformMatrices */ NULL,
+ /* deformVertsEM */ NULL,
+ /* deformMatricesEM */ NULL,
+ /* applyModifier */ applyModifier,
+ /* applyModifierEM */ NULL,
+ /* initData */ initData,
+ /* requiredDataMask */ NULL,
+ /* freeData */ NULL,
+ /* isDisabled */ isDisabled,
+ /* updateDepgraph */ updateDepgraph,
+ /* updateDepsgraph */ updateDepsgraph,
+ /* dependsOnTime */ NULL,
+ /* dependsOnNormals */ NULL,
+ /* foreachObjectLink */ foreachObjectLink,
+ /* foreachIDLink */ NULL,
+ /* foreachTexLink */ NULL,
+};
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
new file mode 100644
index 00000000000..d8cccca415c
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -0,0 +1,241 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 by the Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Daniel Dunbar
+ * Ton Roosendaal,
+ * Ben Batt,
+ * Brecht Van Lommel,
+ * Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file blender/modifiers/intern/MOD_particlesystem.c
+ * \ingroup modifiers
+ */
+
+
+#include <stddef.h>
+
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BLI_utildefines.h"
+
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_modifier.h"
+#include "BKE_particle.h"
+
+#include "MOD_util.h"
+
+
+static void initData(ModifierData *md)
+{
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md;
+ psmd->psys = NULL;
+ psmd->dm_final = NULL;
+ psmd->dm_deformed = NULL;
+ psmd->totdmvert = psmd->totdmedge = psmd->totdmface = 0;
+}
+static void freeData(ModifierData *md)
+{
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md;
+
+ if (psmd->dm_final) {
+ psmd->dm_final->needsFree = true;
+ psmd->dm_final->release(psmd->dm_final);
+ psmd->dm_final = NULL;
+ if (psmd->dm_deformed) {
+ psmd->dm_deformed->needsFree = true;
+ psmd->dm_deformed->release(psmd->dm_deformed);
+ psmd->dm_deformed = NULL;
+ }
+ }
+
+ /* ED_object_modifier_remove may have freed this first before calling
+ * modifier_free (which calls this function) */
+ if (psmd->psys)
+ psmd->psys->flag |= PSYS_DELETE;
+}
+static void copyData(ModifierData *md, ModifierData *target)
+{
+#if 0
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md;
+#endif
+ ParticleSystemModifierData *tpsmd = (ParticleSystemModifierData *) target;
+
+ modifier_copyData_generic(md, target);
+
+ tpsmd->dm_final = NULL;
+ tpsmd->dm_deformed = NULL;
+ tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0;
+}
+
+static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
+{
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md;
+ return psys_emitter_customdata_mask(psmd->psys);
+}
+
+/* saves the current emitter state for a particle system and calculates particles */
+static void deformVerts(ModifierData *md, Object *ob,
+ DerivedMesh *derivedData,
+ float (*vertexCos)[3],
+ int UNUSED(numVerts),
+ ModifierApplyFlag flag)
+{
+ DerivedMesh *dm = derivedData;
+ ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md;
+ ParticleSystem *psys = NULL;
+ bool needsFree = false;
+ /* float cfra = BKE_scene_frame_get(md->scene); */ /* UNUSED */
+
+ if (ob->particlesystem.first)
+ psys = psmd->psys;
+ else
+ return;
+
+ if (!psys_check_enabled(ob, psys, (flag & MOD_APPLY_RENDER) != 0))
+ return;
+
+ if (dm == NULL) {
+ dm = get_dm(ob, NULL, NULL, vertexCos, false, true);
+
+ if (!dm)
+ return;
+
+ needsFree = true;
+ }
+
+ /* clear old dm */
+ if (psmd->dm_final) {
+ psmd->dm_final->needsFree = true;
+ psmd->dm_final->release(psmd->dm_final);
+ if (psmd->dm_deformed) {
+ psmd->dm_deformed->needsFree = 1;
+ psmd->dm_deformed->release(psmd->dm_deformed);
+ psmd->dm_deformed = NULL;
+ }
+ }
+ else if (psmd->flag & eParticleSystemFlag_file_loaded) {
+ /* in file read dm just wasn't saved in file so no need to reset everything */
+ psmd->flag &= ~eParticleSystemFlag_file_loaded;
+ }
+ else {
+ /* no dm before, so recalc particles fully */
+ psys->recalc |= PSYS_RECALC_RESET;
+ }
+
+ /* make new dm */
+ psmd->dm_final = CDDM_copy(dm);
+ CDDM_apply_vert_coords(psmd->dm_final, vertexCos);
+ CDDM_calc_normals(psmd->dm_final);
+
+ if (needsFree) {
+ dm->needsFree = true;
+ dm->release(dm);
+ }
+
+ /* protect dm */
+ psmd->dm_final->needsFree = false;
+
+ DM_ensure_tessface(psmd->dm_final);
+
+ if (!psmd->dm_final->deformedOnly) {
+ /* XXX Think we can assume here that if current DM is not only-deformed, ob->deformedOnly has been set.
+ * This is awfully weak though. :| */
+ if (ob->derivedDeform) {
+ psmd->dm_deformed = CDDM_copy(ob->derivedDeform);
+ }
+ else { /* Can happen in some cases, e.g. when rendering from Edit mode... */
+ psmd->dm_deformed = CDDM_from_mesh((Mesh *)ob->data);
+ }
+ DM_ensure_tessface(psmd->dm_deformed);
+ }
+
+ /* report change in mesh structure */
+ if (psmd->dm_final->getNumVerts(psmd->dm_final) != psmd->totdmvert ||
+ psmd->dm_final->getNumEdges(psmd->dm_final) != psmd->totdmedge ||
+ psmd->dm_final->getNumTessFaces(psmd->dm_final) != psmd->totdmface)
+ {
+ psys->recalc |= PSYS_RECALC_RESET;
+
+ psmd->totdmvert = psmd->dm_final->getNumVerts(psmd->dm_final);
+ psmd->totdmedge = psmd->dm_final->getNumEdges(psmd->dm_final);
+ psmd->totdmface = psmd->dm_final->getNumTessFaces(psmd->dm_final);
+ }
+
+ if (!(ob->transflag & OB_NO_PSYS_UPDATE)) {
+ psmd->flag &= ~eParticleSystemFlag_psys_updated;
+ particle_system_update(md->scene, ob, psys, (flag & MOD_APPLY_RENDER) != 0);
+ psmd->flag |= eParticleSystemFlag_psys_updated;
+ }
+}
+
+/* disabled particles in editmode for now, until support for proper derivedmesh
+ * updates is coded */
+#if 0
+static void deformVertsEM(
+ ModifierData *md, Object *ob, EditMesh *editData,
+ DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts)
+{
+ DerivedMesh *dm = derivedData;
+
+ if (!derivedData) dm = CDDM_from_editmesh(editData, ob->data);
+
+ deformVerts(md, ob, dm, vertexCos, numVerts);
+
+ if (!derivedData) dm->release(dm);
+}
+#endif
+
+
+ModifierTypeInfo modifierType_ParticleSystem = {
+ /* name */ "ParticleSystem",
+ /* structName */ "ParticleSystemModifierData",
+ /* structSize */ sizeof(ParticleSystemModifierData),
+ /* type */ eModifierTypeType_OnlyDeform,
+ /* flags */ eModifierTypeFlag_AcceptsMesh |
+ eModifierTypeFlag_SupportsMapping |
+ eModifierTypeFlag_UsesPointCache /* |
+ eModifierTypeFlag_SupportsEditmode |
+ eModifierTypeFlag_EnableInEditmode */,
+
+ /* copyData */ copyData,
+ /* deformVerts */ deformVerts,
+ /* deformVertsEM */ NULL,
+ /* deformMatrices */ NULL,
+ /* deformMatricesEM */ NULL,
+ /* applyModifier */ NULL,
+ /* applyModifierEM */ NULL,
+ /* initData */ initData,
+ /* requiredDataMask */ requiredDataMask,
+ /* freeData */ freeData,
+ /* isDisabled */ NULL,
+ /* updateDepgraph */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* dependsOnNormals */ NULL,
+ /* foreachObjectLink */ NULL,
+ /* foreachIDLink */ NULL,
+ /* foreachTexLink */ NULL,
+};
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index 4ccf9fabf0c..97aae733532 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -35,12 +35,12 @@
#include "BLI_math.h"
#include "DNA_key_types.h"
-#include "DNA_object_types.h"
#include "BLI_utildefines.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_key.h"
+#include "BKE_particle.h"
#include "MOD_modifiertypes.h"
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index 66c9f613447..d45c8528510 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -34,7 +34,6 @@
#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -42,6 +41,7 @@
#include "MEM_guardedalloc.h"
#include "BKE_cdderivedmesh.h"
+#include "BKE_particle.h"
#include "BKE_deform.h"
#include "MOD_modifiertypes.h"
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 101f5a4f619..17adc7f1520 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -34,13 +34,13 @@
#include <stdio.h>
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_force.h"
#include "BLI_utildefines.h"
#include "BKE_cdderivedmesh.h"
+#include "BKE_particle.h"
#include "BKE_softbody.h"
#include "depsgraph_private.h"
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 9eb7e4e83b6..911b6997058 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -32,7 +32,6 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
@@ -43,6 +42,7 @@
#include "BKE_cdderivedmesh.h"
#include "BKE_mesh.h"
+#include "BKE_particle.h"
#include "BKE_deform.h"
#include "MOD_modifiertypes.h"
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index a59ace130e7..93414562ccf 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -257,6 +257,8 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(Boolean);
INIT_TYPE(MeshDeform);
INIT_TYPE(Ocean);
+ INIT_TYPE(ParticleSystem);
+ INIT_TYPE(ParticleInstance);
INIT_TYPE(Explode);
INIT_TYPE(Shrinkwrap);
INIT_TYPE(Fluidsim);
diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.c b/source/blender/nodes/shader/nodes/node_shader_particle_info.c
index dc19416d688..5f0d81e98c9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_particle_info.c
+++ b/source/blender/nodes/shader/nodes/node_shader_particle_info.c
@@ -50,7 +50,12 @@ static void node_shader_exec_particle_info(void *data, int UNUSED(thread), bNode
static int gpu_shader_particle_info(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
{
- return GPU_stack_link(mat, "particle_info", in, out);
+
+ return GPU_stack_link(mat, "particle_info", in, out,
+ GPU_builtin(GPU_PARTICLE_SCALAR_PROPS),
+ GPU_builtin(GPU_PARTICLE_LOCATION),
+ GPU_builtin(GPU_PARTICLE_VELOCITY),
+ GPU_builtin(GPU_PARTICLE_ANG_VELOCITY));
}
/* node type definition */
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
index 5f87f9b03d6..86961cdd169 100644
--- a/source/blender/render/intern/source/convertblender.c
+++ b/source/blender/render/intern/source/convertblender.c
@@ -57,6 +57,7 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_object_fluidsim.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
@@ -78,6 +79,7 @@
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "PIL_time.h"
@@ -747,6 +749,1148 @@ static Material *give_render_material(Render *re, Object *ob, short nr)
}
/* ------------------------------------------------------------------------- */
+/* Particles */
+/* ------------------------------------------------------------------------- */
+typedef struct ParticleStrandData {
+ struct MCol *mcol;
+ float *orco, *uvco, *surfnor;
+ float time, adapt_angle, adapt_pix, size;
+ int totuv, totcol;
+ int first, line, adapt, override_uv;
+}
+ParticleStrandData;
+/* future thread problem... */
+static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, ParticleStrandData *sd, const float vec[3], const float vec1[3])
+{
+ static VertRen *v1= NULL, *v2= NULL;
+ VlakRen *vlr= NULL;
+ float nor[3], cross[3], crosslen, w, dx, dy, width;
+ static float anor[3], avec[3];
+ int flag, i;
+ static int second=0;
+
+ sub_v3_v3v3(nor, vec, vec1);
+ normalize_v3(nor); /* nor needed as tangent */
+ cross_v3_v3v3(cross, vec, nor);
+
+ /* turn cross in pixelsize */
+ w= vec[2]*re->winmat[2][3] + re->winmat[3][3];
+ dx= re->winx*cross[0]*re->winmat[0][0];
+ dy= re->winy*cross[1]*re->winmat[1][1];
+ w = sqrtf(dx * dx + dy * dy) / w;
+
+ if (w!=0.0f) {
+ float fac;
+ if (ma->strand_ease!=0.0f) {
+ if (ma->strand_ease<0.0f)
+ fac= pow(sd->time, 1.0f+ma->strand_ease);
+ else
+ fac= pow(sd->time, 1.0f/(1.0f-ma->strand_ease));
+ }
+ else fac= sd->time;
+
+ width= ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end);
+
+ /* use actual Blender units for strand width and fall back to minimum width */
+ if (ma->mode & MA_STR_B_UNITS) {
+ crosslen= len_v3(cross);
+ w= 2.0f*crosslen*ma->strand_min/w;
+
+ if (width < w)
+ width= w;
+
+ /*cross is the radius of the strand so we want it to be half of full width */
+ mul_v3_fl(cross, 0.5f/crosslen);
+ }
+ else
+ width/=w;
+
+ mul_v3_fl(cross, width);
+ }
+
+ if (ma->mode & MA_TANGENT_STR)
+ flag= R_SMOOTH|R_TANGENT;
+ else
+ flag= R_SMOOTH;
+
+ /* only 1 pixel wide strands filled in as quads now, otherwise zbuf errors */
+ if (ma->strand_sta==1.0f)
+ flag |= R_STRAND;
+
+ /* single face line */
+ if (sd->line) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->flag= flag;
+ vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ copy_v3_v3(vlr->v1->co, vec);
+ add_v3_v3(vlr->v1->co, cross);
+ copy_v3_v3(vlr->v1->n, nor);
+ vlr->v1->orco= sd->orco;
+ vlr->v1->accum = -1.0f; /* accum abuse for strand texco */
+
+ copy_v3_v3(vlr->v2->co, vec);
+ sub_v3_v3v3(vlr->v2->co, vlr->v2->co, cross);
+ copy_v3_v3(vlr->v2->n, nor);
+ vlr->v2->orco= sd->orco;
+ vlr->v2->accum= vlr->v1->accum;
+
+ copy_v3_v3(vlr->v4->co, vec1);
+ add_v3_v3(vlr->v4->co, cross);
+ copy_v3_v3(vlr->v4->n, nor);
+ vlr->v4->orco= sd->orco;
+ vlr->v4->accum = 1.0f; /* accum abuse for strand texco */
+
+ copy_v3_v3(vlr->v3->co, vec1);
+ sub_v3_v3v3(vlr->v3->co, vlr->v3->co, cross);
+ copy_v3_v3(vlr->v3->n, nor);
+ vlr->v3->orco= sd->orco;
+ vlr->v3->accum= vlr->v4->accum;
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V2V3;
+
+ if (sd->surfnor) {
+ float *snor= RE_vlakren_get_surfnor(obr, vlr, 1);
+ copy_v3_v3(snor, sd->surfnor);
+ }
+
+ if (sd->uvco) {
+ for (i=0; i<sd->totuv; i++) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, i, NULL, 1);
+ mtf->uv[0][0]=mtf->uv[1][0]=
+ mtf->uv[2][0]=mtf->uv[3][0]=(sd->uvco+2*i)[0];
+ mtf->uv[0][1]=mtf->uv[1][1]=
+ mtf->uv[2][1]=mtf->uv[3][1]=(sd->uvco+2*i)[1];
+ }
+ if (sd->override_uv>=0) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, sd->override_uv, NULL, 0);
+
+ mtf->uv[0][0]=mtf->uv[3][0]=0.0f;
+ mtf->uv[1][0]=mtf->uv[2][0]=1.0f;
+
+ mtf->uv[0][1]=mtf->uv[1][1]=0.0f;
+ mtf->uv[2][1]=mtf->uv[3][1]=1.0f;
+ }
+ }
+ if (sd->mcol) {
+ for (i=0; i<sd->totcol; i++) {
+ MCol *mc;
+ mc=RE_vlakren_get_mcol(obr, vlr, i, NULL, 1);
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ }
+ }
+ }
+ /* first two vertices of a strand */
+ else if (sd->first) {
+ if (sd->adapt) {
+ copy_v3_v3(anor, nor);
+ copy_v3_v3(avec, vec);
+ second=1;
+ }
+
+ v1= RE_findOrAddVert(obr, obr->totvert++);
+ v2= RE_findOrAddVert(obr, obr->totvert++);
+
+ copy_v3_v3(v1->co, vec);
+ add_v3_v3(v1->co, cross);
+ copy_v3_v3(v1->n, nor);
+ v1->orco= sd->orco;
+ v1->accum = -1.0f; /* accum abuse for strand texco */
+
+ copy_v3_v3(v2->co, vec);
+ sub_v3_v3v3(v2->co, v2->co, cross);
+ copy_v3_v3(v2->n, nor);
+ v2->orco= sd->orco;
+ v2->accum= v1->accum;
+ }
+ /* more vertices & faces to strand */
+ else {
+ if (sd->adapt==0 || second) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->flag= flag;
+ vlr->v1= v1;
+ vlr->v2= v2;
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ v1= vlr->v4; /* cycle */
+ v2= vlr->v3; /* cycle */
+
+
+ if (sd->adapt) {
+ second=0;
+ copy_v3_v3(anor, nor);
+ copy_v3_v3(avec, vec);
+ }
+
+ }
+ else if (sd->adapt) {
+ float dvec[3], pvec[3];
+ sub_v3_v3v3(dvec, avec, vec);
+ project_v3_v3v3(pvec, dvec, vec);
+ sub_v3_v3v3(dvec, dvec, pvec);
+
+ w= vec[2]*re->winmat[2][3] + re->winmat[3][3];
+ dx= re->winx*dvec[0]*re->winmat[0][0]/w;
+ dy= re->winy*dvec[1]*re->winmat[1][1]/w;
+ w = sqrtf(dx * dx + dy * dy);
+ if (dot_v3v3(anor, nor)<sd->adapt_angle && w>sd->adapt_pix) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->flag= flag;
+ vlr->v1= v1;
+ vlr->v2= v2;
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ v1= vlr->v4; /* cycle */
+ v2= vlr->v3; /* cycle */
+
+ copy_v3_v3(anor, nor);
+ copy_v3_v3(avec, vec);
+ }
+ else {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak-1);
+ }
+ }
+
+ copy_v3_v3(vlr->v4->co, vec);
+ add_v3_v3(vlr->v4->co, cross);
+ copy_v3_v3(vlr->v4->n, nor);
+ vlr->v4->orco= sd->orco;
+ vlr->v4->accum= -1.0f + 2.0f * sd->time; /* accum abuse for strand texco */
+
+ copy_v3_v3(vlr->v3->co, vec);
+ sub_v3_v3v3(vlr->v3->co, vlr->v3->co, cross);
+ copy_v3_v3(vlr->v3->n, nor);
+ vlr->v3->orco= sd->orco;
+ vlr->v3->accum= vlr->v4->accum;
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V2V3;
+
+ if (sd->surfnor) {
+ float *snor= RE_vlakren_get_surfnor(obr, vlr, 1);
+ copy_v3_v3(snor, sd->surfnor);
+ }
+
+ if (sd->uvco) {
+ for (i=0; i<sd->totuv; i++) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, i, NULL, 1);
+ mtf->uv[0][0]=mtf->uv[1][0]=
+ mtf->uv[2][0]=mtf->uv[3][0]=(sd->uvco+2*i)[0];
+ mtf->uv[0][1]=mtf->uv[1][1]=
+ mtf->uv[2][1]=mtf->uv[3][1]=(sd->uvco+2*i)[1];
+ }
+ if (sd->override_uv>=0) {
+ MTFace *mtf;
+ mtf=RE_vlakren_get_tface(obr, vlr, sd->override_uv, NULL, 0);
+
+ mtf->uv[0][0]=mtf->uv[3][0]=0.0f;
+ mtf->uv[1][0]=mtf->uv[2][0]=1.0f;
+
+ mtf->uv[0][1]=mtf->uv[1][1]=(vlr->v1->accum+1.0f)/2.0f;
+ mtf->uv[2][1]=mtf->uv[3][1]=(vlr->v3->accum+1.0f)/2.0f;
+ }
+ }
+ if (sd->mcol) {
+ for (i=0; i<sd->totcol; i++) {
+ MCol *mc;
+ mc=RE_vlakren_get_mcol(obr, vlr, i, NULL, 1);
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
+ }
+ }
+ }
+}
+
+static void static_particle_wire(ObjectRen *obr, Material *ma, const float vec[3], const float vec1[3], int first, int line)
+{
+ VlakRen *vlr;
+ static VertRen *v1;
+
+ if (line) {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= vlr->v2;
+ vlr->v4= NULL;
+
+ copy_v3_v3(vlr->v1->co, vec);
+ copy_v3_v3(vlr->v2->co, vec1);
+
+ sub_v3_v3v3(vlr->n, vec, vec1);
+ normalize_v3(vlr->n);
+ copy_v3_v3(vlr->v1->n, vlr->n);
+ copy_v3_v3(vlr->v2->n, vlr->n);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V1V2;
+
+ }
+ else if (first) {
+ v1= RE_findOrAddVert(obr, obr->totvert++);
+ copy_v3_v3(v1->co, vec);
+ }
+ else {
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= v1;
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= vlr->v2;
+ vlr->v4= NULL;
+
+ v1= vlr->v2; /* cycle */
+ copy_v3_v3(v1->co, vec);
+
+ sub_v3_v3v3(vlr->n, vec, vec1);
+ normalize_v3(vlr->n);
+ copy_v3_v3(v1->n, vlr->n);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V1V2;
+ }
+
+}
+
+static void particle_curve(Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, ParticleStrandData *sd,
+ const float loc[3], const float loc1[3], int seed, float *pa_co)
+{
+ HaloRen *har = NULL;
+
+ if (ma->material_type == MA_TYPE_WIRE)
+ static_particle_wire(obr, ma, loc, loc1, sd->first, sd->line);
+ else if (ma->material_type == MA_TYPE_HALO) {
+ har= RE_inithalo_particle(re, obr, dm, ma, loc, loc1, sd->orco, sd->uvco, sd->size, 1.0, seed, pa_co);
+ if (har) har->lay= obr->ob->lay;
+ }
+ else
+ static_particle_strand(re, obr, ma, sd, loc, loc1);
+}
+static void particle_billboard(Render *re, ObjectRen *obr, Material *ma, ParticleBillboardData *bb)
+{
+ VlakRen *vlr;
+ MTFace *mtf;
+ float xvec[3], yvec[3], zvec[3], bb_center[3];
+ /* Number of tiles */
+ int totsplit = bb->uv_split * bb->uv_split;
+ int tile, x, y;
+ /* Tile offsets */
+ float uvx = 0.0f, uvy = 0.0f, uvdx = 1.0f, uvdy = 1.0f, time = 0.0f;
+
+ vlr= RE_findOrAddVlak(obr, obr->totvlak++);
+ vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
+ vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
+
+ psys_make_billboard(bb, xvec, yvec, zvec, bb_center);
+
+ add_v3_v3v3(vlr->v1->co, bb_center, xvec);
+ add_v3_v3(vlr->v1->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v1->co);
+
+ sub_v3_v3v3(vlr->v2->co, bb_center, xvec);
+ add_v3_v3(vlr->v2->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v2->co);
+
+ sub_v3_v3v3(vlr->v3->co, bb_center, xvec);
+ sub_v3_v3v3(vlr->v3->co, vlr->v3->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v3->co);
+
+ add_v3_v3v3(vlr->v4->co, bb_center, xvec);
+ sub_v3_v3(vlr->v4->co, yvec);
+ mul_m4_v3(re->viewmat, vlr->v4->co);
+
+ normal_quad_v3(vlr->n, vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co);
+ copy_v3_v3(vlr->v1->n, vlr->n);
+ copy_v3_v3(vlr->v2->n, vlr->n);
+ copy_v3_v3(vlr->v3->n, vlr->n);
+ copy_v3_v3(vlr->v4->n, vlr->n);
+
+ vlr->mat= ma;
+ vlr->ec= ME_V2V3;
+
+ if (bb->uv_split > 1) {
+ uvdx = uvdy = 1.0f / (float)bb->uv_split;
+
+ if (ELEM(bb->anim, PART_BB_ANIM_AGE, PART_BB_ANIM_FRAME)) {
+ if (bb->anim == PART_BB_ANIM_FRAME)
+ time = ((int)(bb->time * bb->lifetime) % totsplit)/(float)totsplit;
+ else
+ time = bb->time;
+ }
+ else if (bb->anim == PART_BB_ANIM_ANGLE) {
+ if (bb->align == PART_BB_VIEW) {
+ time = (float)fmod((bb->tilt + 1.0f) / 2.0f, 1.0);
+ }
+ else {
+ float axis1[3] = {0.0f, 0.0f, 0.0f};
+ float axis2[3] = {0.0f, 0.0f, 0.0f};
+
+ axis1[(bb->align + 1) % 3] = 1.0f;
+ axis2[(bb->align + 2) % 3] = 1.0f;
+
+ if (bb->lock == 0) {
+ zvec[bb->align] = 0.0f;
+ normalize_v3(zvec);
+ }
+
+ time = saacos(dot_v3v3(zvec, axis1)) / (float)M_PI;
+
+ if (dot_v3v3(zvec, axis2) < 0.0f)
+ time = 1.0f - time / 2.0f;
+ else
+ time /= 2.0f;
+ }
+ }
+
+ if (bb->split_offset == PART_BB_OFF_LINEAR)
+ time = (float)fmod(time + (float)bb->num / (float)totsplit, 1.0f);
+ else if (bb->split_offset==PART_BB_OFF_RANDOM)
+ time = (float)fmod(time + bb->random, 1.0f);
+
+ /* Find the coordinates in tile space (integer), then convert to UV
+ * space (float). Note that Y is flipped. */
+ tile = (int)((time + FLT_EPSILON10) * totsplit);
+ x = tile % bb->uv_split;
+ y = tile / bb->uv_split;
+ y = (bb->uv_split - 1) - y;
+ uvx = uvdx * x;
+ uvy = uvdy * y;
+ }
+
+ /* normal UVs */
+ if (bb->uv[0] >= 0) {
+ mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[0], NULL, 1);
+ mtf->uv[0][0] = 1.0f;
+ mtf->uv[0][1] = 1.0f;
+ mtf->uv[1][0] = 0.0f;
+ mtf->uv[1][1] = 1.0f;
+ mtf->uv[2][0] = 0.0f;
+ mtf->uv[2][1] = 0.0f;
+ mtf->uv[3][0] = 1.0f;
+ mtf->uv[3][1] = 0.0f;
+ }
+
+ /* time-index UVs */
+ if (bb->uv[1] >= 0) {
+ mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[1], NULL, 1);
+ mtf->uv[0][0] = mtf->uv[1][0] = mtf->uv[2][0] = mtf->uv[3][0] = bb->time;
+ mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = (float)bb->num/(float)bb->totnum;
+ }
+
+ /* split UVs */
+ if (bb->uv_split > 1 && bb->uv[2] >= 0) {
+ mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[2], NULL, 1);
+ mtf->uv[0][0] = uvx + uvdx;
+ mtf->uv[0][1] = uvy + uvdy;
+ mtf->uv[1][0] = uvx;
+ mtf->uv[1][1] = uvy + uvdy;
+ mtf->uv[2][0] = uvx;
+ mtf->uv[2][1] = uvy;
+ mtf->uv[3][0] = uvx + uvdx;
+ mtf->uv[3][1] = uvy;
+ }
+}
+static void particle_normal_ren(short ren_as, ParticleSettings *part, Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, ParticleStrandData *sd, ParticleBillboardData *bb, ParticleKey *state, int seed, float hasize, float *pa_co)
+{
+ float loc[3], loc0[3], loc1[3], vel[3];
+
+ copy_v3_v3(loc, state->co);
+
+ if (ren_as != PART_DRAW_BB)
+ mul_m4_v3(re->viewmat, loc);
+
+ switch (ren_as) {
+ case PART_DRAW_LINE:
+ sd->line = 1;
+ sd->time = 0.0f;
+ sd->size = hasize;
+
+ mul_v3_mat3_m4v3(vel, re->viewmat, state->vel);
+ normalize_v3(vel);
+
+ if (part->draw & PART_DRAW_VEL_LENGTH)
+ mul_v3_fl(vel, len_v3(state->vel));
+
+ madd_v3_v3v3fl(loc0, loc, vel, -part->draw_line[0]);
+ madd_v3_v3v3fl(loc1, loc, vel, part->draw_line[1]);
+
+ particle_curve(re, obr, dm, ma, sd, loc0, loc1, seed, pa_co);
+
+ break;
+
+ case PART_DRAW_BB:
+
+ copy_v3_v3(bb->vec, loc);
+ copy_v3_v3(bb->vel, state->vel);
+
+ particle_billboard(re, obr, ma, bb);
+
+ break;
+
+ default:
+ {
+ HaloRen *har = NULL;
+
+ har = RE_inithalo_particle(re, obr, dm, ma, loc, NULL, sd->orco, sd->uvco, hasize, 0.0, seed, pa_co);
+
+ if (har) har->lay= obr->ob->lay;
+
+ break;
+ }
+ }
+}
+static void get_particle_uvco_mcol(short from, DerivedMesh *dm, float *fuv, int num, ParticleStrandData *sd)
+{
+ int i;
+
+ /* get uvco */
+ if (sd->uvco && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ for (i=0; i<sd->totuv; i++) {
+ if (!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ MFace *mface = dm->getTessFaceData(dm, num, CD_MFACE);
+ MTFace *mtface = (MTFace*)CustomData_get_layer_n(&dm->faceData, CD_MTFACE, i);
+ mtface += num;
+
+ psys_interpolate_uvs(mtface, mface->v4, fuv, sd->uvco + 2 * i);
+ }
+ else {
+ sd->uvco[2*i] = 0.0f;
+ sd->uvco[2*i + 1] = 0.0f;
+ }
+ }
+ }
+
+ /* get mcol */
+ if (sd->mcol && ELEM(from, PART_FROM_FACE, PART_FROM_VOLUME)) {
+ for (i=0; i<sd->totcol; i++) {
+ if (!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ MFace *mface = dm->getTessFaceData(dm, num, CD_MFACE);
+ MCol *mc = (MCol*)CustomData_get_layer_n(&dm->faceData, CD_MCOL, i);
+ mc += num * 4;
+
+ psys_interpolate_mcol(mc, mface->v4, fuv, sd->mcol + i);
+ }
+ else
+ memset(&sd->mcol[i], 0, sizeof(MCol));
+ }
+ }
+}
+static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem *psys, int timeoffset)
+{
+ Object *ob= obr->ob;
+// Object *tob=0;
+ Material *ma = NULL;
+ ParticleSystemModifierData *psmd;
+ ParticleSystem *tpsys = NULL;
+ ParticleSettings *part, *tpart = NULL;
+ ParticleData *pars, *pa = NULL, *tpa = NULL;
+ ParticleKey *states = NULL;
+ ParticleKey state;
+ ParticleCacheKey *cache = NULL;
+ ParticleBillboardData bb;
+ ParticleSimulationData sim = {NULL};
+ ParticleStrandData sd;
+ StrandBuffer *strandbuf = NULL;
+ StrandVert *svert = NULL;
+ StrandBound *sbound = NULL;
+ StrandRen *strand = NULL;
+ RNG *rng = NULL;
+ float loc[3], loc1[3], loc0[3], mat[4][4], nmat[3][3], co[3], nor[3], duplimat[4][4];
+ float strandlen=0.0f, curlen=0.0f;
+ float hasize, pa_size, r_tilt, r_length;
+ float pa_time, pa_birthtime, pa_dietime;
+ float random, simplify[2], pa_co[3];
+ const float cfra= BKE_scene_frame_get(re->scene);
+ int i, a, k, max_k=0, totpart;
+ bool do_simplify = false, do_surfacecache = false, use_duplimat = false;
+ int totchild=0, step_nbr;
+ int seed, path_nbr=0, orco1=0, num;
+ int totface;
+
+ const int *index_mf_to_mpoly = NULL;
+ const int *index_mp_to_orig = NULL;
+
+/* 1. check that everything is ok & updated */
+ if (psys==NULL)
+ return 0;
+
+ part=psys->part;
+ pars=psys->particles;
+
+ if (part==NULL || pars==NULL || !psys_check_enabled(ob, psys, G.is_rendering))
+ return 0;
+
+ if (part->ren_as==PART_DRAW_OB || part->ren_as==PART_DRAW_GR || part->ren_as==PART_DRAW_NOT)
+ return 1;
+
+ if ((re->r.scemode & R_VIEWPORT_PREVIEW) && (ob->mode & OB_MODE_PARTICLE_EDIT))
+ return 0;
+
+ if (part->ren_as == PART_DRAW_BB && part->bb_ob == NULL && RE_GetCamera(re) == NULL)
+ return 0;
+
+/* 2. start initializing things */
+
+ /* last possibility to bail out! */
+ psmd = psys_get_modifier(ob, psys);
+ if (!(psmd->modifier.mode & eModifierMode_Render))
+ return 0;
+
+ sim.scene= re->scene;
+ sim.ob= ob;
+ sim.psys= psys;
+ sim.psmd= psmd;
+
+ if (part->phystype==PART_PHYS_KEYED)
+ psys_count_keyed_targets(&sim);
+
+ totchild=psys->totchild;
+
+ /* can happen for disconnected/global hair */
+ if (part->type==PART_HAIR && !psys->childcache)
+ totchild= 0;
+
+ if (re->r.scemode & R_VIEWPORT_PREVIEW) { /* preview render */
+ totchild = (int)((float)totchild * (float)part->disp / 100.0f);
+ step_nbr = 1 << part->draw_step;
+ }
+ else {
+ step_nbr = 1 << part->ren_step;
+ }
+ if (ELEM(part->kink, PART_KINK_SPIRAL))
+ step_nbr += part->kink_extra_steps;
+
+ psys->flag |= PSYS_DRAWING;
+
+ rng= BLI_rng_new(psys->seed);
+
+ totpart=psys->totpart;
+
+ memset(&sd, 0, sizeof(ParticleStrandData));
+ sd.override_uv = -1;
+
+/* 2.1 setup material stff */
+ ma= give_render_material(re, ob, part->omat);
+
+#if 0 /* XXX old animation system */
+ if (ma->ipo) {
+ calc_ipo(ma->ipo, cfra);
+ execute_ipo((ID *)ma, ma->ipo);
+ }
+#endif /* XXX old animation system */
+
+ hasize = ma->hasize;
+ seed = ma->seed1;
+
+ re->flag |= R_HALO;
+
+ RE_set_customdata_names(obr, &psmd->dm_final->faceData);
+ sd.totuv = CustomData_number_of_layers(&psmd->dm_final->faceData, CD_MTFACE);
+ sd.totcol = CustomData_number_of_layers(&psmd->dm_final->faceData, CD_MCOL);
+
+ if (ma->texco & TEXCO_UV && sd.totuv) {
+ sd.uvco = MEM_callocN(sd.totuv * 2 * sizeof(float), "particle_uvs");
+
+ if (ma->strand_uvname[0]) {
+ sd.override_uv = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, ma->strand_uvname);
+ sd.override_uv -= CustomData_get_layer_index(&psmd->dm_final->faceData, CD_MTFACE);
+ }
+ }
+ else
+ sd.uvco = NULL;
+
+ if (sd.totcol)
+ sd.mcol = MEM_callocN(sd.totcol * sizeof(MCol), "particle_mcols");
+
+/* 2.2 setup billboards */
+ if (part->ren_as == PART_DRAW_BB) {
+ int first_uv = CustomData_get_layer_index(&psmd->dm_final->faceData, CD_MTFACE);
+
+ bb.uv[0] = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, psys->bb_uvname[0]);
+ if (bb.uv[0] < 0)
+ bb.uv[0] = CustomData_get_active_layer_index(&psmd->dm_final->faceData, CD_MTFACE);
+
+ bb.uv[1] = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, psys->bb_uvname[1]);
+
+ bb.uv[2] = CustomData_get_named_layer_index(&psmd->dm_final->faceData, CD_MTFACE, psys->bb_uvname[2]);
+
+ if (first_uv >= 0) {
+ bb.uv[0] -= first_uv;
+ bb.uv[1] -= first_uv;
+ bb.uv[2] -= first_uv;
+ }
+
+ bb.align = part->bb_align;
+ bb.anim = part->bb_anim;
+ bb.lock = part->draw & PART_DRAW_BB_LOCK;
+ bb.ob = (part->bb_ob ? part->bb_ob : RE_GetCamera(re));
+ bb.split_offset = part->bb_split_offset;
+ bb.totnum = totpart+totchild;
+ bb.uv_split = part->bb_uv_split;
+ }
+
+/* 2.5 setup matrices */
+ mul_m4_m4m4(mat, re->viewmat, ob->obmat);
+ invert_m4_m4(ob->imat, mat); /* need to be that way, for imat texture */
+ transpose_m3_m4(nmat, ob->imat);
+
+ if (psys->flag & PSYS_USE_IMAT) {
+ /* psys->imat is the original emitter's inverse matrix, ob->obmat is the duplicated object's matrix */
+ mul_m4_m4m4(duplimat, ob->obmat, psys->imat);
+ use_duplimat = true;
+ }
+
+/* 2.6 setup strand rendering */
+ if (part->ren_as == PART_DRAW_PATH && psys->pathcache) {
+ path_nbr = step_nbr;
+
+ if (path_nbr) {
+ if (!ELEM(ma->material_type, MA_TYPE_HALO, MA_TYPE_WIRE)) {
+ sd.orco = get_object_orco(re, psys);
+ if (!sd.orco) {
+ sd.orco = MEM_mallocN(3*sizeof(float)*(totpart+totchild), "particle orcos");
+ set_object_orco(re, psys, sd.orco);
+ }
+ }
+ }
+
+ if (part->draw & PART_DRAW_REN_ADAPT) {
+ sd.adapt = 1;
+ sd.adapt_pix = (float)part->adapt_pix;
+ sd.adapt_angle = cosf(DEG2RADF((float)part->adapt_angle));
+ }
+
+ if (part->draw & PART_DRAW_REN_STRAND) {
+ strandbuf= RE_addStrandBuffer(obr, (totpart+totchild)*(path_nbr+1));
+ strandbuf->ma= ma;
+ strandbuf->lay= ob->lay;
+ copy_m4_m4(strandbuf->winmat, re->winmat);
+ strandbuf->winx= re->winx;
+ strandbuf->winy= re->winy;
+ strandbuf->maxdepth= 2;
+ strandbuf->adaptcos= cosf(DEG2RADF((float)part->adapt_angle));
+ strandbuf->overrideuv= sd.override_uv;
+ strandbuf->minwidth= ma->strand_min;
+
+ if (ma->strand_widthfade == 0.0f)
+ strandbuf->widthfade= -1.0f;
+ else if (ma->strand_widthfade >= 1.0f)
+ strandbuf->widthfade= 2.0f - ma->strand_widthfade;
+ else
+ strandbuf->widthfade= 1.0f/MAX2(ma->strand_widthfade, 1e-5f);
+
+ if (part->flag & PART_HAIR_BSPLINE)
+ strandbuf->flag |= R_STRAND_BSPLINE;
+ if (ma->mode & MA_STR_B_UNITS)
+ strandbuf->flag |= R_STRAND_B_UNITS;
+
+ svert= strandbuf->vert;
+
+ if (re->r.mode & R_SPEED)
+ do_surfacecache = true;
+ else if ((re->wrld.mode & (WO_AMB_OCC|WO_ENV_LIGHT|WO_INDIRECT_LIGHT)) && (re->wrld.ao_gather_method == WO_AOGATHER_APPROX))
+ if (ma->amb != 0.0f)
+ do_surfacecache = true;
+
+ totface= psmd->dm_final->getNumTessFaces(psmd->dm_final);
+ index_mf_to_mpoly = psmd->dm_final->getTessFaceDataArray(psmd->dm_final, CD_ORIGINDEX);
+ index_mp_to_orig = psmd->dm_final->getPolyDataArray(psmd->dm_final, CD_ORIGINDEX);
+ if (index_mf_to_mpoly == NULL) {
+ index_mp_to_orig = NULL;
+ }
+ for (a=0; a<totface; a++)
+ strandbuf->totbound = max_ii(strandbuf->totbound, (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a): a);
+
+ strandbuf->totbound++;
+ strandbuf->bound= MEM_callocN(sizeof(StrandBound)*strandbuf->totbound, "StrandBound");
+ sbound= strandbuf->bound;
+ sbound->start= sbound->end= 0;
+ }
+ }
+
+ if (sd.orco == NULL) {
+ sd.orco = MEM_mallocN(3 * sizeof(float), "particle orco");
+ orco1 = 1;
+ }
+
+ if (path_nbr == 0)
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+/* 3. start creating renderable things */
+ for (a=0, pa=pars; a<totpart+totchild; a++, pa++, seed++) {
+ random = BLI_rng_get_float(rng);
+ /* setup per particle individual stuff */
+ if (a<totpart) {
+ if (pa->flag & PARS_UNEXIST) continue;
+
+ pa_time=(cfra-pa->time)/pa->lifetime;
+ pa_birthtime = pa->time;
+ pa_dietime = pa->dietime;
+
+ hasize = ma->hasize;
+
+ /* XXX 'tpsys' is alwyas NULL, this code won't run! */
+ /* get orco */
+ if (tpsys && part->phystype == PART_PHYS_NO) {
+ tpa = tpsys->particles + pa->num;
+ psys_particle_on_emitter(
+ psmd,
+ tpart->from, tpa->num, pa->num_dmcache, tpa->fuv,
+ tpa->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+ else {
+ psys_particle_on_emitter(
+ psmd,
+ part->from, pa->num, pa->num_dmcache,
+ pa->fuv, pa->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+
+ /* get uvco & mcol */
+ num= pa->num_dmcache;
+
+ if (num == DMCACHE_NOTFOUND)
+ if (pa->num < psmd->dm_final->getNumTessFaces(psmd->dm_final))
+ num= pa->num;
+
+ get_particle_uvco_mcol(part->from, psmd->dm_final, pa->fuv, num, &sd);
+
+ pa_size = pa->size;
+
+ r_tilt = 2.0f*(psys_frand(psys, a) - 0.5f);
+ r_length = psys_frand(psys, a+1);
+
+ if (path_nbr) {
+ cache = psys->pathcache[a];
+ max_k = (int)cache->segments;
+ }
+
+ if (totchild && (part->draw&PART_DRAW_PARENT)==0) continue;
+ }
+ else {
+ ChildParticle *cpa= psys->child+a-totpart;
+
+ if (path_nbr) {
+ cache = psys->childcache[a-totpart];
+
+ if (cache->segments < 0)
+ continue;
+
+ max_k = (int)cache->segments;
+ }
+
+ pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);
+ pa_size = psys_get_child_size(psys, cpa, cfra, &pa_time);
+
+ r_tilt = 2.0f*(psys_frand(psys, a + 21) - 0.5f);
+ r_length = psys_frand(psys, a + 22);
+
+ num = cpa->num;
+
+ /* get orco */
+ if (part->childtype == PART_CHILD_FACES) {
+ psys_particle_on_emitter(
+ psmd,
+ PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD,
+ cpa->fuv, cpa->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+ else {
+ ParticleData *par = psys->particles + cpa->parent;
+ psys_particle_on_emitter(
+ psmd,
+ part->from, par->num, DMCACHE_ISCHILD, par->fuv,
+ par->foffset, co, nor, NULL, NULL, sd.orco, NULL);
+ }
+
+ /* get uvco & mcol */
+ if (part->childtype==PART_CHILD_FACES) {
+ get_particle_uvco_mcol(PART_FROM_FACE, psmd->dm_final, cpa->fuv, cpa->num, &sd);
+ }
+ else {
+ ParticleData *parent = psys->particles + cpa->parent;
+ num = parent->num_dmcache;
+
+ if (num == DMCACHE_NOTFOUND)
+ if (parent->num < psmd->dm_final->getNumTessFaces(psmd->dm_final))
+ num = parent->num;
+
+ get_particle_uvco_mcol(part->from, psmd->dm_final, parent->fuv, num, &sd);
+ }
+
+ do_simplify = psys_render_simplify_params(psys, cpa, simplify);
+
+ if (strandbuf) {
+ int orignum = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, cpa->num) : cpa->num;
+
+ if ((orignum > sbound - strandbuf->bound) &&
+ (orignum < strandbuf->totbound))
+ {
+ sbound = &strandbuf->bound[orignum];
+ sbound->start = sbound->end = obr->totstrand;
+ }
+ }
+ }
+
+ /* TEXCO_PARTICLE */
+ pa_co[0] = pa_time;
+ pa_co[1] = 0.f;
+ pa_co[2] = 0.f;
+
+ /* surface normal shading setup */
+ if (ma->mode_l & MA_STR_SURFDIFF) {
+ mul_m3_v3(nmat, nor);
+ sd.surfnor= nor;
+ }
+ else
+ sd.surfnor= NULL;
+
+ /* strand render setup */
+ if (strandbuf) {
+ strand= RE_findOrAddStrand(obr, obr->totstrand++);
+ strand->buffer= strandbuf;
+ strand->vert= svert;
+ copy_v3_v3(strand->orco, sd.orco);
+
+ if (do_simplify) {
+ float *ssimplify= RE_strandren_get_simplify(obr, strand, 1);
+ ssimplify[0]= simplify[0];
+ ssimplify[1]= simplify[1];
+ }
+
+ if (sd.surfnor) {
+ float *snor= RE_strandren_get_surfnor(obr, strand, 1);
+ copy_v3_v3(snor, sd.surfnor);
+ }
+
+ if (do_surfacecache && num >= 0) {
+ int *facenum= RE_strandren_get_face(obr, strand, 1);
+ *facenum= num;
+ }
+
+ if (sd.uvco) {
+ for (i=0; i<sd.totuv; i++) {
+ if (i != sd.override_uv) {
+ float *uv= RE_strandren_get_uv(obr, strand, i, NULL, 1);
+
+ uv[0]= sd.uvco[2*i];
+ uv[1]= sd.uvco[2*i+1];
+ }
+ }
+ }
+ if (sd.mcol) {
+ for (i=0; i<sd.totcol; i++) {
+ MCol *mc= RE_strandren_get_mcol(obr, strand, i, NULL, 1);
+ *mc = sd.mcol[i];
+ }
+ }
+
+ sbound->end++;
+ }
+
+ /* strandco computation setup */
+ if (path_nbr) {
+ strandlen= 0.0f;
+ curlen= 0.0f;
+ for (k=1; k<=path_nbr; k++)
+ if (k<=max_k)
+ strandlen += len_v3v3((cache+k-1)->co, (cache+k)->co);
+ }
+
+ if (path_nbr) {
+ /* render strands */
+ for (k=0; k<=path_nbr; k++) {
+ float time;
+
+ if (k<=max_k) {
+ copy_v3_v3(state.co, (cache+k)->co);
+ copy_v3_v3(state.vel, (cache+k)->vel);
+ }
+ else
+ continue;
+
+ if (k > 0)
+ curlen += len_v3v3((cache+k-1)->co, (cache+k)->co);
+ time= curlen/strandlen;
+
+ copy_v3_v3(loc, state.co);
+ mul_m4_v3(re->viewmat, loc);
+
+ if (strandbuf) {
+ copy_v3_v3(svert->co, loc);
+ svert->strandco= -1.0f + 2.0f*time;
+ svert++;
+ strand->totvert++;
+ }
+ else {
+ sd.size = hasize;
+
+ if (k==1) {
+ sd.first = 1;
+ sd.time = 0.0f;
+ sub_v3_v3v3(loc0, loc1, loc);
+ add_v3_v3v3(loc0, loc1, loc0);
+
+ particle_curve(re, obr, psmd->dm_final, ma, &sd, loc1, loc0, seed, pa_co);
+ }
+
+ sd.first = 0;
+ sd.time = time;
+
+ if (k)
+ particle_curve(re, obr, psmd->dm_final, ma, &sd, loc, loc1, seed, pa_co);
+
+ copy_v3_v3(loc1, loc);
+ }
+ }
+
+ }
+ else {
+ /* render normal particles */
+ if (part->trail_count > 1) {
+ float length = part->path_end * (1.0f - part->randlength * r_length);
+ int trail_count = part->trail_count * (1.0f - part->randlength * r_length);
+ float ct = (part->draw & PART_ABS_PATH_TIME) ? cfra : pa_time;
+ float dt = length / (trail_count ? (float)trail_count : 1.0f);
+
+ /* make sure we have pointcache in memory before getting particle on path */
+ psys_make_temp_pointcache(ob, psys);
+
+ for (i=0; i < trail_count; i++, ct -= dt) {
+ if (part->draw & PART_ABS_PATH_TIME) {
+ if (ct < pa_birthtime || ct > pa_dietime)
+ continue;
+ }
+ else if (ct < 0.0f || ct > 1.0f)
+ continue;
+
+ state.time = (part->draw & PART_ABS_PATH_TIME) ? -ct : ct;
+ psys_get_particle_on_path(&sim, a, &state, 1);
+
+ if (psys->parent)
+ mul_m4_v3(psys->parent->obmat, state.co);
+
+ if (use_duplimat)
+ mul_m4_v4(duplimat, state.co);
+
+ if (part->ren_as == PART_DRAW_BB) {
+ bb.random = random;
+ bb.offset[0] = part->bb_offset[0];
+ bb.offset[1] = part->bb_offset[1];
+ bb.size[0] = part->bb_size[0] * pa_size;
+ if (part->bb_align==PART_BB_VEL) {
+ float pa_vel = len_v3(state.vel);
+ float head = part->bb_vel_head*pa_vel;
+ float tail = part->bb_vel_tail*pa_vel;
+ bb.size[1] = part->bb_size[1]*pa_size + head + tail;
+ /* use offset to adjust the particle center. this is relative to size, so need to divide! */
+ if (bb.size[1] > 0.0f)
+ bb.offset[1] += (head-tail) / bb.size[1];
+ }
+ else
+ bb.size[1] = part->bb_size[1] * pa_size;
+ bb.tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
+ bb.time = ct;
+ bb.num = a;
+ }
+
+ pa_co[0] = (part->draw & PART_ABS_PATH_TIME) ? (ct-pa_birthtime)/(pa_dietime-pa_birthtime) : ct;
+ pa_co[1] = (float)i/(float)(trail_count-1);
+
+ particle_normal_ren(part->ren_as, part, re, obr, psmd->dm_final, ma, &sd, &bb, &state, seed, hasize, pa_co);
+ }
+ }
+ else {
+ state.time=cfra;
+ if (psys_get_particle_state(&sim, a, &state, 0)==0)
+ continue;
+
+ if (psys->parent)
+ mul_m4_v3(psys->parent->obmat, state.co);
+
+ if (use_duplimat)
+ mul_m4_v3(duplimat, state.co);
+
+ if (part->ren_as == PART_DRAW_BB) {
+ bb.random = random;
+ bb.offset[0] = part->bb_offset[0];
+ bb.offset[1] = part->bb_offset[1];
+ bb.size[0] = part->bb_size[0] * pa_size;
+ if (part->bb_align==PART_BB_VEL) {
+ float pa_vel = len_v3(state.vel);
+ float head = part->bb_vel_head*pa_vel;
+ float tail = part->bb_vel_tail*pa_vel;
+ bb.size[1] = part->bb_size[1]*pa_size + head + tail;
+ /* use offset to adjust the particle center. this is relative to size, so need to divide! */
+ if (bb.size[1] > 0.0f)
+ bb.offset[1] += (head-tail) / bb.size[1];
+ }
+ else
+ bb.size[1] = part->bb_size[1] * pa_size;
+ bb.tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
+ bb.time = pa_time;
+ bb.num = a;
+ bb.lifetime = pa_dietime-pa_birthtime;
+ }
+
+ particle_normal_ren(part->ren_as, part, re, obr, psmd->dm_final, ma, &sd, &bb, &state, seed, hasize, pa_co);
+ }
+ }
+
+ if (orco1==0)
+ sd.orco+=3;
+
+ if (re->test_break(re->tbh))
+ break;
+ }
+
+ if (do_surfacecache)
+ strandbuf->surface= cache_strand_surface(re, obr, psmd->dm_final, mat, timeoffset);
+
+/* 4. clean up */
+#if 0 /* XXX old animation system */
+ if (ma) do_mat_ipo(re->scene, ma);
+#endif /* XXX old animation system */
+
+ if (orco1)
+ MEM_freeN(sd.orco);
+
+ if (sd.uvco)
+ MEM_freeN(sd.uvco);
+
+ if (sd.mcol)
+ MEM_freeN(sd.mcol);
+
+ if (states)
+ MEM_freeN(states);
+
+ BLI_rng_free(rng);
+
+ psys->flag &= ~PSYS_DRAWING;
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (path_nbr && (ma->mode_l & MA_TANGENT_STR)==0)
+ calc_vertexnormals(re, obr, 1, 0, 0);
+
+ return 1;
+}
+
+/* ------------------------------------------------------------------------- */
/* Halo's */
/* ------------------------------------------------------------------------- */
@@ -3464,15 +4608,38 @@ static void set_dupli_tex_mat(Render *re, ObjectInstanceRen *obi, DupliObject *d
static void init_render_object_data(Render *re, ObjectRen *obr, int timeoffset)
{
Object *ob= obr->ob;
+ ParticleSystem *psys;
+ int i;
+
+ if (obr->psysindex) {
+ if ((!obr->prev || obr->prev->ob != ob || (obr->prev->flag & R_INSTANCEABLE)==0) && ob->type==OB_MESH) {
+ /* the emitter mesh wasn't rendered so the modifier stack wasn't
+ * evaluated with render settings */
+ DerivedMesh *dm;
+ const CustomDataMask mask = CD_MASK_RENDER_INTERNAL;
- if (ELEM(ob->type, OB_FONT, OB_CURVE))
- init_render_curve(re, obr, timeoffset);
- else if (ob->type==OB_SURF)
- init_render_surf(re, obr, timeoffset);
- else if (ob->type==OB_MESH)
- init_render_mesh(re, obr, timeoffset);
- else if (ob->type==OB_MBALL)
- init_render_mball(re, obr);
+ if (re->r.scemode & R_VIEWPORT_PREVIEW)
+ dm = mesh_create_derived_view(re->scene, ob, mask);
+ else
+ dm = mesh_create_derived_render(re->scene, ob, mask);
+ dm->release(dm);
+ }
+
+ for (psys=ob->particlesystem.first, i=0; i<obr->psysindex-1; i++)
+ psys= psys->next;
+
+ render_new_particle_system(re, obr, psys, timeoffset);
+ }
+ else {
+ if (ELEM(ob->type, OB_FONT, OB_CURVE))
+ init_render_curve(re, obr, timeoffset);
+ else if (ob->type==OB_SURF)
+ init_render_surf(re, obr, timeoffset);
+ else if (ob->type==OB_MESH)
+ init_render_mesh(re, obr, timeoffset);
+ else if (ob->type==OB_MBALL)
+ init_render_mball(re, obr);
+ }
finalize_render_object(re, obr, timeoffset);
@@ -3486,10 +4653,26 @@ static void add_render_object(Render *re, Object *ob, Object *par, DupliObject *
{
ObjectRen *obr;
ObjectInstanceRen *obi;
- int allow_render= 1, index, i;
+ ParticleSystem *psys;
+ int show_emitter, allow_render= 1, index, psysindex, i;
index= (dob)? dob->persistent_id[0]: 0;
+ /* the emitter has to be processed first (render levels of modifiers) */
+ /* so here we only check if the emitter should be rendered */
+ if (ob->particlesystem.first) {
+ show_emitter= 0;
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ show_emitter += psys->part->draw & PART_DRAW_EMITTER;
+ if (!(re->r.scemode & R_VIEWPORT_PREVIEW))
+ psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset);
+ }
+
+ /* if no psys has "show emitter" selected don't render emitter */
+ if (show_emitter == 0)
+ allow_render= 0;
+ }
+
/* one render object for the data itself */
if (allow_render) {
obr= RE_addRenderObject(re, ob, par, index, 0, ob->lay);
@@ -3513,6 +4696,35 @@ static void add_render_object(Render *re, Object *ob, Object *par, DupliObject *
add_volume(re, obr, ma);
}
}
+
+ /* and one render object per particle system */
+ if (ob->particlesystem.first) {
+ psysindex= 1;
+ for (psys=ob->particlesystem.first; psys; psys=psys->next, psysindex++) {
+ if (!psys_check_enabled(ob, psys, G.is_rendering))
+ continue;
+
+ obr= RE_addRenderObject(re, ob, par, index, psysindex, ob->lay);
+ if ((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) {
+ obr->flag |= R_INSTANCEABLE;
+ copy_m4_m4(obr->obmat, ob->obmat);
+ }
+ if (dob)
+ psys->flag |= PSYS_USE_IMAT;
+ init_render_object_data(re, obr, timeoffset);
+ if (!(re->r.scemode & R_VIEWPORT_PREVIEW))
+ psys_render_restore(ob, psys);
+ psys->flag &= ~PSYS_USE_IMAT;
+
+ /* only add instance for objects that have not been used for dupli */
+ if (!(ob->transflag & OB_RENDER_DUPLI)) {
+ obi = RE_addRenderInstance(re, obr, ob, par, index, psysindex, NULL, ob->lay, dob);
+ if (dob) set_dupli_tex_mat(re, obi, dob, omat);
+ }
+ else
+ find_dupli_instances(re, obr, dob);
+ }
+ }
}
/* par = pointer to duplicator parent, needed for object lookup table */
@@ -3633,8 +4845,11 @@ static int allow_render_object(Render *re, Object *ob, int nolamps, int onlysele
if (is_object_hidden(re, ob))
return 0;
- if ((ob->transflag & OB_DUPLI) && !(ob->transflag & OB_DUPLIFRAMES)) {
- return 0;
+ /* Only handle dupli-hiding here if there is no particle systems. Else, let those handle show/noshow. */
+ if (!ob->particlesystem.first) {
+ if ((ob->transflag & OB_DUPLI) && !(ob->transflag & OB_DUPLIFRAMES)) {
+ return 0;
+ }
}
/* don't add non-basic meta objects, ends up having renderobjects with no geometry */
@@ -3652,6 +4867,7 @@ static int allow_render_object(Render *re, Object *ob, int nolamps, int onlysele
static int allow_render_dupli_instance(Render *UNUSED(re), DupliObject *dob, Object *obd)
{
+ ParticleSystem *psys;
Material *ma;
short a, *totmaterial;
@@ -3667,6 +4883,10 @@ static int allow_render_dupli_instance(Render *UNUSED(re), DupliObject *dob, Obj
}
}
+ for (psys=obd->particlesystem.first; psys; psys=psys->next)
+ if (!ELEM(psys->part->ren_as, PART_DRAW_BB, PART_DRAW_LINE, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR))
+ return 0;
+
/* don't allow lamp, animated duplis, or radio render */
return (render_object_type(obd->type) &&
(!(dob->type == OB_DUPLIGROUP) || !dob->animated));
@@ -3678,12 +4898,36 @@ static void dupli_render_particle_set(Render *re, Object *ob, int timeoffset, in
* settings before calling object_duplilist, to get render level duplis */
Group *group;
GroupObject *go;
+ ParticleSystem *psys;
+ DerivedMesh *dm;
if (re->r.scemode & R_VIEWPORT_PREVIEW)
return;
if (level >= MAX_DUPLI_RECUR)
return;
+
+ if (ob->transflag & OB_DUPLIPARTS) {
+ for (psys=ob->particlesystem.first; psys; psys=psys->next) {
+ if (ELEM(psys->part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
+ if (enable)
+ psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset);
+ else
+ psys_render_restore(ob, psys);
+ }
+ }
+
+ if (enable) {
+ /* this is to make sure we get render level duplis in groups:
+ * the derivedmesh must be created before init_render_mesh,
+ * since object_duplilist does dupliparticles before that */
+ dm = mesh_create_derived_render(re->scene, ob, CD_MASK_RENDER_INTERNAL);
+ dm->release(dm);
+
+ for (psys=ob->particlesystem.first; psys; psys=psys->next)
+ psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated;
+ }
+ }
if (ob->dup_group==NULL) return;
group= ob->dup_group;
@@ -3822,7 +5066,9 @@ static void database_init_objects(Render *re, unsigned int renderlay, int nolamp
continue;
if (allow_render_dupli_instance(re, dob, obd)) {
+ ParticleSystem *psys;
ObjectRen *obr = NULL;
+ int psysindex;
float mat[4][4];
obi=NULL;
@@ -3853,6 +5099,29 @@ static void database_init_objects(Render *re, unsigned int renderlay, int nolamp
}
}
+ /* same logic for particles, each particle system has it's own object, so
+ * need to go over them separately */
+ psysindex= 1;
+ for (psys=obd->particlesystem.first; psys; psys=psys->next) {
+ if (dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, obd, psysindex))) {
+ if (obi == NULL)
+ mul_m4_m4m4(mat, re->viewmat, dob->mat);
+ obi = RE_addRenderInstance(re, NULL, obd, ob, dob->persistent_id[0], psysindex++, mat, obd->lay, dob);
+
+ set_dupli_tex_mat(re, obi, dob, dob_extra->obmat);
+ if (dob->type != OB_DUPLIGROUP) {
+ copy_v3_v3(obi->dupliorco, dob->orco);
+ obi->dupliuv[0]= dob->uv[0];
+ obi->dupliuv[1]= dob->uv[1];
+ }
+ else {
+ assign_dupligroup_dupli(re, obi, obr, dob);
+ if (obd->transflag & OB_RENDER_DUPLI)
+ find_dupli_instances(re, obr, dob);
+ }
+ }
+ }
+
if (obi==NULL)
/* can't instance, just create the object */
init_render_object(re, obd, ob, dob, dob_extra->obmat, timeoffset);
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index badc438b826..9f1ae4a96e0 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -70,6 +70,7 @@
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
+#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_sequencer.h"
@@ -3089,6 +3090,21 @@ static void validate_render_settings(Render *re)
}
}
+static void update_physics_cache(Render *re, Scene *scene, int UNUSED(anim_init))
+{
+ PTCacheBaker baker;
+
+ memset(&baker, 0, sizeof(baker));
+ baker.main = re->main;
+ baker.scene = scene;
+ baker.bake = 0;
+ baker.render = 1;
+ baker.anim_init = 1;
+ baker.quick_step = 1;
+
+ BKE_ptcache_bake(&baker);
+}
+
void RE_SetActiveRenderView(Render *re, const char *viewname)
{
BLI_strncpy(re->viewname, viewname, sizeof(re->viewname));
@@ -3101,7 +3117,7 @@ const char *RE_GetActiveRenderView(Render *re)
/* evaluating scene options for general Blender render */
static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, Scene *scene, SceneRenderLayer *srl,
- Object *camera_override, unsigned int lay_override, int anim, int UNUSED(anim_init))
+ Object *camera_override, unsigned int lay_override, int anim, int anim_init)
{
int winx, winy;
rcti disprect;
@@ -3145,6 +3161,16 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
/* check all scenes involved */
tag_scenes_for_render(re);
+
+ /*
+ * Disabled completely for now,
+ * can be later set as render profile option
+ * and default for background render.
+ */
+ if (0) {
+ /* make sure dynamics are up to date */
+ update_physics_cache(re, scene, anim_init);
+ }
if (srl || scene->r.scemode & R_SINGLE_LAYER) {
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c
index ffb44cf6826..a03ea9cb896 100644
--- a/source/blender/render/intern/source/pointdensity.c
+++ b/source/blender/render/intern/source/pointdensity.c
@@ -45,6 +45,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_texture_types.h"
#include "BKE_deform.h"
@@ -52,6 +53,7 @@
#include "BKE_lattice.h"
#include "BKE_main.h"
#include "BKE_object.h"
+#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
#include "BKE_colortools.h"
@@ -165,6 +167,149 @@ static void alloc_point_data(PointDensity *pd)
}
}
+static void pointdensity_cache_psys(Scene *scene,
+ PointDensity *pd,
+ Object *ob,
+ ParticleSystem *psys,
+ float viewmat[4][4],
+ float winmat[4][4],
+ int winx, int winy,
+ const bool use_render_params)
+{
+ DerivedMesh *dm;
+ ParticleKey state;
+ ParticleCacheKey *cache;
+ ParticleSimulationData sim = {NULL};
+ ParticleData *pa = NULL;
+ float cfra = BKE_scene_frame_get(scene);
+ int i /*, childexists*/ /* UNUSED */;
+ int total_particles;
+ int data_used;
+ float *data_vel, *data_life;
+ float partco[3];
+
+ /* init everything */
+ if (!psys || !ob || !pd) {
+ return;
+ }
+
+ data_used = point_data_used(pd);
+
+ /* Just to create a valid rendering context for particles */
+ if (use_render_params) {
+ psys_render_set(ob, psys, viewmat, winmat, winx, winy, 0);
+ }
+
+ if (use_render_params) {
+ dm = mesh_create_derived_render(scene,
+ ob,
+ CD_MASK_BAREMESH | CD_MASK_MTFACE | CD_MASK_MCOL);
+ }
+ else {
+ dm = mesh_get_derived_final(scene,
+ ob,
+ CD_MASK_BAREMESH | CD_MASK_MTFACE | CD_MASK_MCOL);
+ }
+
+ if (!psys_check_enabled(ob, psys, use_render_params)) {
+ psys_render_restore(ob, psys);
+ return;
+ }
+
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(ob, psys);
+
+ /* in case ob->imat isn't up-to-date */
+ invert_m4_m4(ob->imat, ob->obmat);
+
+ total_particles = psys->totpart + psys->totchild;
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6);
+ pd->totpoints = total_particles;
+ alloc_point_data(pd);
+ point_data_pointers(pd, &data_vel, &data_life, NULL);
+
+#if 0 /* UNUSED */
+ if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT))
+ childexists = 1;
+#endif
+
+ for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) {
+
+ if (psys->part->type == PART_HAIR) {
+ /* hair particles */
+ if (i < psys->totpart && psys->pathcache)
+ cache = psys->pathcache[i];
+ else if (i >= psys->totpart && psys->childcache)
+ cache = psys->childcache[i - psys->totpart];
+ else
+ continue;
+
+ cache += cache->segments; /* use endpoint */
+
+ copy_v3_v3(state.co, cache->co);
+ zero_v3(state.vel);
+ state.time = 0.0f;
+ }
+ else {
+ /* emitter particles */
+ state.time = cfra;
+
+ if (!psys_get_particle_state(&sim, i, &state, 0))
+ continue;
+
+ if (data_used & POINT_DATA_LIFE) {
+ if (i < psys->totpart) {
+ state.time = (cfra - pa->time) / pa->lifetime;
+ }
+ else {
+ ChildParticle *cpa = (psys->child + i) - psys->totpart;
+ float pa_birthtime, pa_dietime;
+
+ state.time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);
+ }
+ }
+ }
+
+ copy_v3_v3(partco, state.co);
+
+ if (pd->psys_cache_space == TEX_PD_OBJECTSPACE)
+ mul_m4_v3(ob->imat, partco);
+ else if (pd->psys_cache_space == TEX_PD_OBJECTLOC) {
+ sub_v3_v3(partco, ob->loc);
+ }
+ else {
+ /* TEX_PD_WORLDSPACE */
+ }
+
+ BLI_bvhtree_insert(pd->point_tree, i, partco, 1);
+
+ if (data_vel) {
+ data_vel[i*3 + 0] = state.vel[0];
+ data_vel[i*3 + 1] = state.vel[1];
+ data_vel[i*3 + 2] = state.vel[2];
+ }
+ if (data_life) {
+ data_life[i] = state.time;
+ }
+ }
+
+ BLI_bvhtree_balance(pd->point_tree);
+ dm->release(dm);
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (use_render_params) {
+ psys_render_restore(ob, psys);
+ }
+}
+
static void pointdensity_cache_vertex_color(PointDensity *pd, Object *UNUSED(ob), DerivedMesh *dm, float *data_color)
{
@@ -332,6 +477,9 @@ static void pointdensity_cache_object(Scene *scene,
static void cache_pointdensity_ex(Scene *scene,
PointDensity *pd,
+ float viewmat[4][4],
+ float winmat[4][4],
+ int winx, int winy,
const bool use_render_params)
{
if (pd == NULL) {
@@ -343,7 +491,28 @@ static void cache_pointdensity_ex(Scene *scene,
pd->point_tree = NULL;
}
- if (pd->source == TEX_PD_OBJECT) {
+ if (pd->source == TEX_PD_PSYS) {
+ Object *ob = pd->object;
+ ParticleSystem *psys;
+
+ if (!ob || !pd->psys) {
+ return;
+ }
+
+ psys = BLI_findlink(&ob->particlesystem, pd->psys - 1);
+ if (!psys) {
+ return;
+ }
+
+ pointdensity_cache_psys(scene,
+ pd,
+ ob,
+ psys,
+ viewmat, winmat,
+ winx, winy,
+ use_render_params);
+ }
+ else if (pd->source == TEX_PD_OBJECT) {
Object *ob = pd->object;
if (ob && ob->type == OB_MESH)
pointdensity_cache_object(scene, pd, ob, use_render_params);
@@ -352,7 +521,11 @@ static void cache_pointdensity_ex(Scene *scene,
void cache_pointdensity(Render *re, PointDensity *pd)
{
- cache_pointdensity_ex(re->scene, pd, true);
+ cache_pointdensity_ex(re->scene,
+ pd,
+ re->viewmat, re->winmat,
+ re->winx, re->winy,
+ true);
}
void free_pointdensity(PointDensity *pd)
@@ -703,20 +876,83 @@ static void sample_dummy_point_density(int resolution, float *values)
memset(values, 0, sizeof(float) * 4 * resolution * resolution * resolution);
}
+static void particle_system_minmax(Scene *scene,
+ Object *object,
+ ParticleSystem *psys,
+ float radius,
+ const bool use_render_params,
+ float min[3], float max[3])
+{
+ const float size[3] = {radius, radius, radius};
+ const float cfra = BKE_scene_frame_get(scene);
+ ParticleSettings *part = psys->part;
+ ParticleSimulationData sim = {NULL};
+ ParticleData *pa = NULL;
+ int i;
+ int total_particles;
+ float mat[4][4], imat[4][4];
+
+ INIT_MINMAX(min, max);
+ if (part->type == PART_HAIR) {
+ /* TOOD(sergey): Not supported currently. */
+ return;
+ }
+
+ unit_m4(mat);
+ if (use_render_params) {
+ psys_render_set(object, psys, mat, mat, 1, 1, 0);
+ }
+
+ sim.scene = scene;
+ sim.ob = object;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(object, psys);
+
+ invert_m4_m4(imat, object->obmat);
+ total_particles = psys->totpart + psys->totchild;
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+ for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) {
+ float co_object[3], co_min[3], co_max[3];
+ ParticleKey state;
+ state.time = cfra;
+ if (!psys_get_particle_state(&sim, i, &state, 0)) {
+ continue;
+ }
+ mul_v3_m4v3(co_object, imat, state.co);
+ sub_v3_v3v3(co_min, co_object, size);
+ add_v3_v3v3(co_max, co_object, size);
+ minmax_v3v3_v3(min, max, co_min);
+ minmax_v3v3_v3(min, max, co_max);
+ }
+
+ if (psys->lattice_deform_data) {
+ end_latt_deform(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
+ }
+
+ if (use_render_params) {
+ psys_render_restore(object, psys);
+ }
+}
+
void RE_point_density_cache(
Scene *scene,
PointDensity *pd,
const bool use_render_params)
{
+ float mat[4][4];
+ /* Same matricies/resolution as dupli_render_particle_set(). */
+ unit_m4(mat);
BLI_mutex_lock(&sample_mutex);
- cache_pointdensity_ex(scene, pd, use_render_params);
+ cache_pointdensity_ex(scene, pd, mat, mat, 1, 1, use_render_params);
BLI_mutex_unlock(&sample_mutex);
}
void RE_point_density_minmax(
- struct Scene *UNUSED(scene),
+ struct Scene *scene,
struct PointDensity *pd,
- const bool UNUSED(use_render_params),
+ const bool use_render_params,
float r_min[3], float r_max[3])
{
Object *object = pd->object;
@@ -725,7 +961,27 @@ void RE_point_density_minmax(
zero_v3(r_max);
return;
}
- if (pd->source == TEX_PD_OBJECT) {
+ if (pd->source == TEX_PD_PSYS) {
+ ParticleSystem *psys;
+ if (pd->psys == 0) {
+ zero_v3(r_min);
+ zero_v3(r_max);
+ return;
+ }
+ psys = BLI_findlink(&object->particlesystem, pd->psys - 1);
+ if (psys == NULL) {
+ zero_v3(r_min);
+ zero_v3(r_max);
+ return;
+ }
+ particle_system_minmax(scene,
+ object,
+ psys,
+ pd->radius,
+ use_render_params,
+ r_min, r_max);
+ }
+ else {
float radius[3] = {pd->radius, pd->radius, pd->radius};
float *loc, *size;
diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c
index 2d6d7244a5f..76e6ca8d467 100644
--- a/source/blender/render/intern/source/renderdatabase.c
+++ b/source/blender/render/intern/source/renderdatabase.c
@@ -71,6 +71,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_texture_types.h"
#include "DNA_listBase.h"
+#include "DNA_particle_types.h"
#include "BKE_customdata.h"
#include "BKE_DerivedMesh.h"
@@ -1415,7 +1416,7 @@ void RE_updateRenderInstances(Render *re, int flag)
ObjectInstanceRen *RE_addRenderInstance(
Render *re, ObjectRen *obr, Object *ob, Object *par,
- int index, int psysindex, float mat[4][4], int lay, const DupliObject *UNUSED(dob))
+ int index, int psysindex, float mat[4][4], int lay, const DupliObject *dob)
{
ObjectInstanceRen *obi;
float mat3[3][3];
@@ -1428,6 +1429,35 @@ ObjectInstanceRen *RE_addRenderInstance(
obi->psysindex= psysindex;
obi->lay= lay;
+ /* Fill particle info */
+ if (par && dob) {
+ const ParticleSystem *psys = dob->particle_system;
+ if (psys) {
+ int part_index;
+ if (obi->index < psys->totpart) {
+ part_index = obi->index;
+ }
+ else if (psys->child) {
+ part_index = psys->child[obi->index - psys->totpart].parent;
+ }
+ else {
+ part_index = -1;
+ }
+
+ if (part_index >= 0) {
+ const ParticleData *p = &psys->particles[part_index];
+ obi->part_index = part_index;
+ obi->part_size = p->size;
+ obi->part_age = RE_GetStats(re)->cfra - p->time;
+ obi->part_lifetime = p->lifetime;
+
+ copy_v3_v3(obi->part_co, p->state.co);
+ copy_v3_v3(obi->part_vel, p->state.vel);
+ copy_v3_v3(obi->part_avel, p->state.ave);
+ }
+ }
+ }
+
RE_updateRenderInstance(re, obi, RE_OBJECT_INSTANCES_UPDATE_OBMAT | RE_OBJECT_INSTANCES_UPDATE_VIEW);
if (mat) {
diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c
index 5fce2930ab1..20602314526 100644
--- a/source/blender/render/intern/source/shadeinput.c
+++ b/source/blender/render/intern/source/shadeinput.c
@@ -39,6 +39,7 @@
#include "DNA_lamp_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_material_types.h"
+#include "DNA_particle_types.h"
#include "BKE_scene.h"
diff --git a/source/blender/render/intern/source/voxeldata.c b/source/blender/render/intern/source/voxeldata.c
index f21ce7795f6..6dbcf474e77 100644
--- a/source/blender/render/intern/source/voxeldata.c
+++ b/source/blender/render/intern/source/voxeldata.c
@@ -63,6 +63,7 @@
#include "DNA_texture_types.h"
#include "DNA_object_force.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_modifier_types.h"
#include "DNA_smoke_types.h"
@@ -225,7 +226,7 @@ static int read_voxeldata_header(FILE *fp, struct VoxelData *vd)
return 1;
}
-static void init_frame_smoke(VoxelData *vd, int UNUSED(cfra))
+static void init_frame_smoke(VoxelData *vd, int cfra)
{
#ifdef WITH_SMOKE
Object *ob;
@@ -248,7 +249,9 @@ static void init_frame_smoke(VoxelData *vd, int UNUSED(cfra))
return;
}
- if (vd->smoked_type == TEX_VD_SMOKEHEAT) {
+ if (cfra < sds->point_cache[0]->startframe)
+ ; /* don't show smoke before simulation starts, this could be made an option in the future */
+ else if (vd->smoked_type == TEX_VD_SMOKEHEAT) {
size_t totRes;
size_t i;
float *heat;
@@ -365,8 +368,20 @@ static void init_frame_smoke(VoxelData *vd, int UNUSED(cfra))
static void init_frame_hair(VoxelData *vd, int UNUSED(cfra))
{
+ Object *ob;
+ ModifierData *md;
+
vd->dataset = NULL;
if (vd->object == NULL) return;
+ ob = vd->object;
+
+ if ((md = (ModifierData *)modifiers_findByType(ob, eModifierType_ParticleSystem))) {
+ ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md;
+
+ if (pmd->psys && pmd->psys->clmd) {
+ vd->ok |= BPH_cloth_solver_get_texture_data(ob, pmd->psys->clmd, vd);
+ }
+ }
}
void cache_voxeldata(Tex *tex, int scene_frame)
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index f51b6b2aafd..3bed4dac2cf 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -300,7 +300,7 @@ typedef struct wmNotifier {
#define ND_MODIFIER (24<<16)
#define ND_KEYS (25<<16)
#define ND_CONSTRAINT (26<<16)
-/*#define ND_PARTICLE (27<<16)*/ /* DEPRECATED */
+#define ND_PARTICLE (27<<16)
#define ND_POINTCACHE (28<<16)
#define ND_PARENT (29<<16)
#define ND_LOD (30<<16)