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