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/intern/pointcache.c
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/intern/pointcache.c')
-rw-r--r--source/blender/blenkernel/intern/pointcache.c4095
1 files changed, 4095 insertions, 0 deletions
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);
+ }
+}