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