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/blenkernel
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/blenkernel')
-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
47 files changed, 19591 insertions, 60 deletions
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)