Fix bug #19699: point density texture doesn't save particle system.
[blender.git] / source / blender / render / intern / source / pointdensity.c
1 /* 
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributors: Matt Ebb
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 #include <math.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_arithb.h"
33 #include "BLI_blenlib.h"
34 #include "BLI_kdopbvh.h"
35
36 #include "BKE_DerivedMesh.h"
37 #include "BKE_global.h"
38 #include "BKE_lattice.h"
39 #include "BKE_main.h"
40 #include "BKE_object.h"
41 #include "BKE_particle.h"
42 #include "BKE_texture.h"
43
44 #include "DNA_meshdata_types.h"
45 #include "DNA_texture_types.h"
46 #include "DNA_particle_types.h"
47
48 #include "render_types.h"
49 #include "renderdatabase.h"
50 #include "texture.h"
51
52 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
53 /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
54 /* only to be used here in this file, it's for speed */
55 extern struct Render R;
56 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
57
58
59 static int point_data_used(PointDensity *pd)
60 {
61         int pd_bitflag = 0;
62         
63         if ((pd->noise_influence == TEX_PD_NOISE_VEL) || (pd->color_source == TEX_PD_COLOR_PARTVEL) || (pd->color_source == TEX_PD_COLOR_PARTSPEED))
64                 pd_bitflag |= POINT_DATA_VEL;
65         if ((pd->noise_influence == TEX_PD_NOISE_AGE) || (pd->color_source == TEX_PD_COLOR_PARTAGE)) 
66                 pd_bitflag |= POINT_DATA_LIFE;
67                 
68         return pd_bitflag;
69 }
70
71
72 /* additional data stored alongside the point density BVH, 
73  * accessible by point index number to retrieve other information 
74  * such as particle velocity or lifetime */
75 static void alloc_point_data(PointDensity *pd, int total_particles, int point_data_used)
76 {
77         int data_size = 0;
78         
79         if (point_data_used & POINT_DATA_VEL) {
80                 /* store 3 channels of velocity data */
81                 data_size += 3;
82         }
83         if (point_data_used & POINT_DATA_LIFE) {
84                 /* store 1 channel of lifetime data */
85                 data_size += 1;
86         }
87
88         if (data_size)
89                 pd->point_data = MEM_mallocN(sizeof(float)*data_size*total_particles, "particle point data");
90 }
91
92 static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, ParticleSystem *psys)
93 {
94         DerivedMesh* dm;
95         ParticleKey state;
96         ParticleSimulationData sim = {re->scene, ob, psys, NULL};
97         ParticleData *pa=NULL;
98         float cfra = bsystem_time(re->scene, ob, (float)re->scene->r.cfra, 0.0);
99         int i, childexists;
100         int total_particles, offset=0;
101         int data_used = point_data_used(pd);
102         float partco[3];
103         float obview[4][4];
104         
105         
106         /* init everything */
107         if (!psys || !ob || !pd) return;
108         
109         Mat4MulMat4(obview, re->viewinv, ob->obmat);
110         
111         /* Just to create a valid rendering context for particles */
112         psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, 0);
113         
114         dm = mesh_create_derived_render(re->scene, ob,CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL);
115         
116         if ( !psys_check_enabled(ob, psys)) {
117                 psys_render_restore(ob, psys);
118                 return;
119         }
120         
121         /* in case ob->imat isn't up-to-date */
122         Mat4Invert(ob->imat, ob->obmat);
123         
124         total_particles = psys->totpart+psys->totchild;
125         psys->lattice=psys_get_lattice(&sim);
126         
127         pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6);
128         alloc_point_data(pd, total_particles, data_used);
129         pd->totpoints = total_particles;
130         if (data_used & POINT_DATA_VEL) offset = pd->totpoints*3;
131         
132         if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT))
133                 childexists = 1;
134         
135         for (i=0, pa=psys->particles; i < total_particles; i++, pa++) {
136
137                 state.time = cfra;
138                 if(psys_get_particle_state(&sim, i, &state, 0)) {
139                         
140                         VECCOPY(partco, state.co);
141                         
142                         if (pd->psys_cache_space == TEX_PD_OBJECTSPACE)
143                                 Mat4MulVecfl(ob->imat, partco);
144                         else if (pd->psys_cache_space == TEX_PD_OBJECTLOC) {
145                                 float obloc[3];
146                                 VECCOPY(obloc, ob->loc);
147                                 VecSubf(partco, partco, obloc);
148                         } else {
149                                 /* TEX_PD_WORLDSPACE */
150                         }
151                         
152                         BLI_bvhtree_insert(pd->point_tree, i, partco, 1);
153                         
154                         if (data_used & POINT_DATA_VEL) {
155                                 pd->point_data[i*3 + 0] = state.vel[0];
156                                 pd->point_data[i*3 + 1] = state.vel[1];
157                                 pd->point_data[i*3 + 2] = state.vel[2];
158                         } 
159                         if (data_used & POINT_DATA_LIFE) {
160                                 float pa_time;
161                                 
162                                 if (i < psys->totpart) {
163                                         pa_time = (cfra - pa->time)/pa->lifetime;
164                                 } else {
165                                         ChildParticle *cpa= (psys->child + i) - psys->totpart;
166                                         float pa_birthtime, pa_dietime;
167                                         
168                                         pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);
169                                 }
170                                 
171                                 pd->point_data[offset + i] = pa_time;
172                         }
173                 }
174         }
175         
176         BLI_bvhtree_balance(pd->point_tree);
177         dm->release(dm);
178         
179         if(psys->lattice){
180                 end_latt_deform(psys->lattice);
181                 psys->lattice=0;
182         }
183         
184         psys_render_restore(ob, psys);
185 }
186
187
188 static void pointdensity_cache_object(Render *re, PointDensity *pd, Object *ob)
189 {
190         int i;
191         DerivedMesh *dm;
192         MVert *mvert = NULL;
193         float cam_mat[4][4];
194         
195         dm = mesh_create_derived_render(re->scene, ob,  CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL);
196         mvert= dm->getVertArray(dm);    /* local object space */
197         
198         pd->totpoints= dm->getNumVerts(dm);
199         if (pd->totpoints == 0) return;
200
201         pd->point_tree = BLI_bvhtree_new(pd->totpoints, 0.0, 4, 6);
202         
203         for(i=0; i < pd->totpoints; i++, mvert++) {
204                 float co[3];
205                 
206                 VECCOPY(co, mvert->co);
207
208                 switch(pd->ob_cache_space) {
209                         case TEX_PD_OBJECTSPACE:
210                                 break;
211                         case TEX_PD_OBJECTLOC:
212                                 Mat4MulVecfl(ob->obmat, co);
213                                 VecSubf(co, co, ob->loc);
214                                 break;
215                         case TEX_PD_WORLDSPACE:
216                         default:
217                                 Mat4MulVecfl(ob->obmat, co);
218                                 break;
219                 }
220
221                 BLI_bvhtree_insert(pd->point_tree, i, co, 1);
222         }
223         
224         BLI_bvhtree_balance(pd->point_tree);
225         dm->release(dm);
226
227 }
228 static void cache_pointdensity(Render *re, Tex *tex)
229 {
230         PointDensity *pd = tex->pd;
231
232         if (pd->point_tree) {
233                 BLI_bvhtree_free(pd->point_tree);
234                 pd->point_tree = NULL;
235         }
236         
237         if (pd->source == TEX_PD_PSYS) {
238                 Object *ob = pd->object;
239                 ParticleSystem *psys;
240
241                 if (!ob || !pd->psys) return;
242
243                 psys= BLI_findlink(&ob->particlesystem, pd->psys-1);
244                 if (!psys) return;
245                 
246                 pointdensity_cache_psys(re, pd, ob, psys);
247         }
248         else if (pd->source == TEX_PD_OBJECT) {
249                 Object *ob = pd->object;
250                 if (ob) pointdensity_cache_object(re, pd, ob);
251         }
252 }
253
254 static void free_pointdensity(Render *re, Tex *tex)
255 {
256         PointDensity *pd = tex->pd;
257
258         if (!pd) return;
259         
260         if (pd->point_tree) {
261                 BLI_bvhtree_free(pd->point_tree);
262                 pd->point_tree = NULL;
263         }
264
265         if (pd->point_data) {
266                 MEM_freeN(pd->point_data);
267                 pd->point_data = NULL;
268         }
269         pd->totpoints = 0;
270 }
271
272
273
274 void make_pointdensities(Render *re)
275 {
276         Tex *tex;
277         
278         if(re->scene->r.scemode & R_PREVIEWBUTS)
279                 return;
280         
281         re->i.infostr= "Caching Point Densities";
282         re->stats_draw(re->sdh, &re->i);
283
284         for (tex= G.main->tex.first; tex; tex= tex->id.next) {
285                 if(tex->id.us && tex->type==TEX_POINTDENSITY) {
286                         cache_pointdensity(re, tex);
287                 }
288         }
289         
290         re->i.infostr= NULL;
291         re->stats_draw(re->sdh, &re->i);
292 }
293
294 void free_pointdensities(Render *re)
295 {
296         Tex *tex;
297         
298         if(re->scene->r.scemode & R_PREVIEWBUTS)
299                 return;
300         
301         for (tex= G.main->tex.first; tex; tex= tex->id.next) {
302                 if(tex->id.us && tex->type==TEX_POINTDENSITY) {
303                         free_pointdensity(re, tex);
304                 }
305         }
306 }
307
308 typedef struct PointDensityRangeData
309 {
310     float *density;
311     float squared_radius;
312     float *point_data;
313         float *vec;
314         float softness;
315     short falloff_type;
316         short noise_influence;
317         float *age;
318         int point_data_used;
319         int offset;
320 } PointDensityRangeData;
321
322 void accum_density(void *userdata, int index, float squared_dist)
323 {
324         PointDensityRangeData *pdr = (PointDensityRangeData *)userdata;
325         const float dist = (pdr->squared_radius - squared_dist) / pdr->squared_radius * 0.5f;
326         float density = 0.0f;
327         
328         if (pdr->falloff_type == TEX_PD_FALLOFF_STD)
329                 density = dist;
330         else if (pdr->falloff_type == TEX_PD_FALLOFF_SMOOTH)
331                 density = 3.0f*dist*dist - 2.0f*dist*dist*dist;
332         else if (pdr->falloff_type == TEX_PD_FALLOFF_SOFT)
333                 density = pow(dist, pdr->softness);
334         else if (pdr->falloff_type == TEX_PD_FALLOFF_CONSTANT)
335                 density = pdr->squared_radius;
336         else if (pdr->falloff_type == TEX_PD_FALLOFF_ROOT)
337                 density = sqrt(dist);
338         
339         if (pdr->point_data_used & POINT_DATA_VEL) {
340                 pdr->vec[0] += pdr->point_data[index*3 + 0]; //* density;
341                 pdr->vec[1] += pdr->point_data[index*3 + 1]; //* density;
342                 pdr->vec[2] += pdr->point_data[index*3 + 2]; //* density;
343         }
344         if (pdr->point_data_used & POINT_DATA_LIFE) {
345                 *pdr->age += pdr->point_data[pdr->offset + index]; // * density;
346         }
347         
348         *pdr->density += density;
349 }
350
351
352 static void init_pointdensityrangedata(PointDensity *pd, PointDensityRangeData *pdr, float *density, float *vec, float *age)
353 {
354         pdr->squared_radius = pd->radius*pd->radius;
355         pdr->density = density;
356         pdr->point_data = pd->point_data;
357         pdr->falloff_type = pd->falloff_type;
358         pdr->vec = vec;
359         pdr->age = age;
360         pdr->softness = pd->falloff_softness;
361         pdr->noise_influence = pd->noise_influence;
362         pdr->point_data_used = point_data_used(pd);
363         pdr->offset = (pdr->point_data_used & POINT_DATA_VEL)?pd->totpoints*3:0;
364 }
365
366
367 int pointdensitytex(Tex *tex, float *texvec, TexResult *texres)
368 {
369         int retval = TEX_INT;
370         PointDensity *pd = tex->pd;
371         PointDensityRangeData pdr;
372         float density=0.0f, age=0.0f, time=0.0f;
373         float vec[3] = {0.0f, 0.0f, 0.0f}, co[3];
374         float col[4];
375         float turb, noise_fac;
376         int num=0;
377         
378         texres->tin = 0.0f;
379         
380         if ((!pd) || (!pd->point_tree))         
381                 return 0;
382                 
383         init_pointdensityrangedata(pd, &pdr, &density, vec, &age);
384         noise_fac = pd->noise_fac * 0.5f;       /* better default */
385         
386         VECCOPY(co, texvec);
387         
388         if (point_data_used(pd)) {
389                 /* does a BVH lookup to find accumulated density and additional point data *
390                  * stores particle velocity vector in 'vec', and particle lifetime in 'time' */
391                 num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr);
392                 if (num > 0) {
393                         age /= num;
394                         VecMulf(vec, 1.0f/num);
395                 }
396                 
397                 /* reset */
398                 density = vec[0] = vec[1] = vec[2] = 0.0f;
399         }
400         
401         if (pd->flag & TEX_PD_TURBULENCE) {
402         
403                 if (pd->noise_influence == TEX_PD_NOISE_AGE) {
404                         turb = BLI_gTurbulence(pd->noise_size, texvec[0]+age, texvec[1]+age, texvec[2]+age, pd->noise_depth, 0, pd->noise_basis);
405                 }
406                 else if (pd->noise_influence == TEX_PD_NOISE_TIME) {
407                         time = R.cfra / (float)R.r.efra;
408                         turb = BLI_gTurbulence(pd->noise_size, texvec[0]+time, texvec[1]+time, texvec[2]+time, pd->noise_depth, 0, pd->noise_basis);
409                         //turb = BLI_turbulence(pd->noise_size, texvec[0]+time, texvec[1]+time, texvec[2]+time, pd->noise_depth);
410                 }
411                 else {
412                         turb = BLI_gTurbulence(pd->noise_size, texvec[0]+vec[0], texvec[1]+vec[1], texvec[2]+vec[2], pd->noise_depth, 0, pd->noise_basis);
413                 }
414
415                 turb -= 0.5f;   /* re-center 0.0-1.0 range around 0 to prevent offsetting result */
416                 
417                 /* now we have an offset coordinate to use for the density lookup */
418                 co[0] = texvec[0] + noise_fac * turb;
419                 co[1] = texvec[1] + noise_fac * turb;
420                 co[2] = texvec[2] + noise_fac * turb;
421         }
422
423         /* BVH query with the potentially perturbed coordinates */
424         num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr);
425         if (num > 0) {
426                 age /= num;
427                 VecMulf(vec, 1.0f/num);
428         }
429         
430         texres->tin = density;
431         BRICONT;
432         
433         if (pd->color_source == TEX_PD_COLOR_CONSTANT)
434                 return retval;
435         
436         retval |= TEX_RGB;
437         
438         switch (pd->color_source) {
439                 case TEX_PD_COLOR_PARTAGE:
440                         if (pd->coba) {
441                                 if (do_colorband(pd->coba, age, col)) {
442                                         texres->talpha= 1;
443                                         VECCOPY(&texres->tr, col);
444                                         texres->tin *= col[3];
445                                         texres->ta = texres->tin;
446                                 }
447                         }
448                         break;
449                 case TEX_PD_COLOR_PARTSPEED:
450                 {
451                         float speed = VecLength(vec) * pd->speed_scale;
452                         
453                         if (pd->coba) {
454                                 if (do_colorband(pd->coba, speed, col)) {
455                                         texres->talpha= 1;      
456                                         VECCOPY(&texres->tr, col);
457                                         texres->tin *= col[3];
458                                         texres->ta = texres->tin;
459                                 }
460                         }
461                         break;
462                 }
463                 case TEX_PD_COLOR_PARTVEL:
464                         texres->talpha= 1;
465                         VecMulf(vec, pd->speed_scale);
466                         VECCOPY(&texres->tr, vec);
467                         texres->ta = texres->tin;
468                         break;
469                 case TEX_PD_COLOR_CONSTANT:
470                 default:
471                         texres->tr = texres->tg = texres->tb = texres->ta = 1.0f;
472                         break;
473         }
474         BRICONTRGB;
475         
476         return retval;
477         
478         /*
479         if (texres->nor!=NULL) {
480                 texres->nor[0] = texres->nor[1] = texres->nor[2] = 0.0f;
481         }
482         */
483 }