6 * ***** BEGIN GPL LICENSE BLOCK *****
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * The Original Code is Copyright (C) Blender Foundation.
23 * All rights reserved.
25 * The Original Code is: all of this file.
27 * Contributor(s): Daniel Genrich
29 * ***** END GPL LICENSE BLOCK *****
32 /** \file blender/blenkernel/intern/smoke.c
37 /* Part of the code copied from elbeem fluid library, copyright by Nils Thuerey */
41 #include "MEM_guardedalloc.h"
46 #include <string.h> /* memset */
48 #include "BLI_linklist.h"
50 #include "BLI_jitter.h"
51 #include "BLI_blenlib.h"
53 #include "BLI_edgehash.h"
54 #include "BLI_kdtree.h"
55 #include "BLI_kdopbvh.h"
56 #include "BLI_utildefines.h"
58 #include "BKE_bvhutils.h"
59 #include "BKE_cdderivedmesh.h"
60 #include "BKE_customdata.h"
61 #include "BKE_DerivedMesh.h"
62 #include "BKE_effect.h"
63 #include "BKE_modifier.h"
64 #include "BKE_particle.h"
65 #include "BKE_pointcache.h"
66 #include "BKE_smoke.h"
69 #include "DNA_customdata_types.h"
70 #include "DNA_group_types.h"
71 #include "DNA_lamp_types.h"
72 #include "DNA_mesh_types.h"
73 #include "DNA_meshdata_types.h"
74 #include "DNA_modifier_types.h"
75 #include "DNA_object_types.h"
76 #include "DNA_particle_types.h"
77 #include "DNA_scene_types.h"
78 #include "DNA_smoke_types.h"
80 #include "smoke_API.h"
82 #include "BKE_smoke.h"
90 static LARGE_INTEGER liFrequency;
91 static LARGE_INTEGER liStartTime;
92 static LARGE_INTEGER liCurrentTime;
94 static void tstart ( void )
96 QueryPerformanceFrequency ( &liFrequency );
97 QueryPerformanceCounter ( &liStartTime );
99 static void tend ( void )
101 QueryPerformanceCounter ( &liCurrentTime );
103 static double tval( void )
105 return ((double)( (liCurrentTime.QuadPart - liStartTime.QuadPart)* (double)1000.0/(double)liFrequency.QuadPart ));
108 #include <sys/time.h>
109 static struct timeval _tstart, _tend;
110 static struct timezone tz;
111 static void tstart ( void )
113 gettimeofday ( &_tstart, &tz );
115 static void tend ( void )
117 gettimeofday ( &_tend,&tz );
124 t1 = ( double ) _tstart.tv_sec*1000 + ( double ) _tstart.tv_usec/ ( 1000 );
125 t2 = ( double ) _tend.tv_sec*1000 + ( double ) _tend.tv_usec/ ( 1000 );
134 struct SmokeModifierData;
136 // forward declerations
137 static void get_cell(float *p0, int res[3], float dx, float *pos, int *cell, int correct);
138 void calcTriangleDivs(Object *ob, MVert *verts, int numverts, MFace *tris, int numfaces, int numtris, int **tridivs, float cell_len);
139 static void fill_scs_points(Object *ob, DerivedMesh *dm, SmokeCollSettings *scs);
141 #define TRI_UVOFFSET (1./4.)
143 static int smokeModifier_init (SmokeModifierData *smd, Object *ob, Scene *scene, DerivedMesh *dm)
145 if((smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain && !smd->domain->fluid)
148 float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX}, max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
150 MVert *verts = dm->getVertArray(dm);
154 res = smd->domain->maxres;
157 for(i = 0; i < dm->getNumVerts(dm); i++)
161 VECCOPY(tmp, verts[i].co);
162 mul_m4_v3(ob->obmat, tmp);
165 min[0] = MIN2(min[0], tmp[0]);
166 min[1] = MIN2(min[1], tmp[1]);
167 min[2] = MIN2(min[2], tmp[2]);
170 max[0] = MAX2(max[0], tmp[0]);
171 max[1] = MAX2(max[1], tmp[1]);
172 max[2] = MAX2(max[2], tmp[2]);
175 VECCOPY(smd->domain->p0, min);
176 VECCOPY(smd->domain->p1, max);
178 // calc other res with max_res provided
179 VECSUB(size, max, min);
181 // printf("size: %f, %f, %f\n", size[0], size[1], size[2]);
183 // prevent crash when initializing a plane as domain
184 if((size[0] < FLT_EPSILON) || (size[1] < FLT_EPSILON) || (size[2] < FLT_EPSILON))
187 if(size[0] > size[1])
189 if(size[0] > size[2])
191 scale = res / size[0];
192 smd->domain->dx = size[0] / res;
193 smd->domain->res[0] = res;
194 smd->domain->res[1] = (int)(size[1] * scale + 0.5);
195 smd->domain->res[2] = (int)(size[2] * scale + 0.5);
199 scale = res / size[2];
200 smd->domain->dx = size[2] / res;
201 smd->domain->res[2] = res;
202 smd->domain->res[0] = (int)(size[0] * scale + 0.5);
203 smd->domain->res[1] = (int)(size[1] * scale + 0.5);
208 if(size[1] > size[2])
210 scale = res / size[1];
211 smd->domain->dx = size[1] / res;
212 smd->domain->res[1] = res;
213 smd->domain->res[0] = (int)(size[0] * scale + 0.5);
214 smd->domain->res[2] = (int)(size[2] * scale + 0.5);
218 scale = res / size[2];
219 smd->domain->dx = size[2] / res;
220 smd->domain->res[2] = res;
221 smd->domain->res[0] = (int)(size[0] * scale + 0.5);
222 smd->domain->res[1] = (int)(size[1] * scale + 0.5);
226 // printf("smd->domain->dx: %f\n", smd->domain->dx);
228 // TODO: put in failsafe if res<=0 - dg
230 // printf("res[0]: %d, res[1]: %d, res[2]: %d\n", smd->domain->res[0], smd->domain->res[1], smd->domain->res[2]);
232 smd->domain->fluid = smoke_init(smd->domain->res, smd->domain->p0);
233 smd->time = scene->r.cfra;
235 if(smd->domain->flags & MOD_SMOKE_HIGHRES)
237 smd->domain->wt = smoke_turbulence_init(smd->domain->res, smd->domain->amplify + 1, smd->domain->noise);
238 smd->domain->res_wt[0] = smd->domain->res[0] * (smd->domain->amplify + 1);
239 smd->domain->res_wt[1] = smd->domain->res[1] * (smd->domain->amplify + 1);
240 smd->domain->res_wt[2] = smd->domain->res[2] * (smd->domain->amplify + 1);
241 smd->domain->dx_wt = smd->domain->dx / (smd->domain->amplify + 1);
242 // printf("smd->domain->amplify: %d\n", smd->domain->amplify);
243 // printf("(smd->domain->flags & MOD_SMOKE_HIGHRES)\n");
246 if(!smd->domain->shadow)
247 smd->domain->shadow = MEM_callocN(sizeof(float) * smd->domain->res[0] * smd->domain->res[1] * smd->domain->res[2], "SmokeDomainShadow");
249 smoke_initBlenderRNA(smd->domain->fluid, &(smd->domain->alpha), &(smd->domain->beta), &(smd->domain->time_scale), &(smd->domain->vorticity), &(smd->domain->border_collisions));
253 smoke_initWaveletBlenderRNA(smd->domain->wt, &(smd->domain->strength));
254 // printf("smoke_initWaveletBlenderRNA\n");
258 else if((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow)
260 // handle flow object here
263 smd->time = scene->r.cfra;
265 // update particle lifetime to be one frame
266 // smd->flow->psys->part->lifetime = scene->r.efra + 1;
270 // smd->flow->bvh = MEM_callocN(sizeof(BVHTreeFromMesh), "smoke_bvhfromfaces");
271 // bvhtree_from_mesh_faces(smd->flow->bvh, dm, 0.0, 2, 6);
274 // copy_m4_m4(smd->flow->mat, ob->obmat);
275 // copy_m4_m4(smd->flow->mat_old, ob->obmat);
281 else if((smd->type & MOD_SMOKE_TYPE_COLL))
283 smd->time = scene->r.cfra;
285 // todo: delete this when loading colls work -dg
287 smokeModifier_createType(smd);
289 if(!smd->coll->points)
291 // init collision points
292 SmokeCollSettings *scs = smd->coll;
295 copy_m4_m4(scs->mat, ob->obmat);
296 copy_m4_m4(scs->mat_old, ob->obmat);
298 fill_scs_points(ob, dm, scs);
301 if(!smd->coll->bvhtree)
303 smd->coll->bvhtree = NULL; // bvhtree_build_from_smoke ( ob->obmat, dm->getFaceArray(dm), dm->getNumFaces(dm), dm->getVertArray(dm), dm->getNumVerts(dm), 0.0 );
311 static void fill_scs_points(Object *ob, DerivedMesh *dm, SmokeCollSettings *scs)
313 MVert *mvert = dm->getVertArray(dm);
314 MFace *mface = dm->getFaceArray(dm);
317 float cell_len = 1.0 / 50.0; // for res = 50
319 int quads = 0, facecounter = 0;
322 for(i = 0; i < dm->getNumFaces(dm); i++)
328 calcTriangleDivs(ob, mvert, dm->getNumVerts(dm), mface, dm->getNumFaces(dm), dm->getNumFaces(dm) + quads, &tridivs, cell_len);
330 // count triangle divisions
331 for(i = 0; i < dm->getNumFaces(dm) + quads; i++)
333 divs += (tridivs[3 * i] + 1) * (tridivs[3 * i + 1] + 1) * (tridivs[3 * i + 2] + 1);
336 // printf("divs: %d\n", divs);
338 scs->points = MEM_callocN(sizeof(float) * (dm->getNumVerts(dm) + divs) * 3, "SmokeCollPoints");
340 for(i = 0; i < dm->getNumVerts(dm); i++)
343 VECCOPY(tmpvec, mvert[i].co);
344 mul_m4_v3(ob->obmat, tmpvec);
345 VECCOPY(&scs->points[i * 3], tmpvec);
348 for(i = 0, facecounter = 0; i < dm->getNumFaces(dm); i++)
354 int divs1 = tridivs[3 * facecounter + 0];
355 int divs2 = tridivs[3 * facecounter + 1];
356 //int divs3 = tridivs[3 * facecounter + 2];
357 float side1[3], side2[3], trinormorg[3], trinorm[3];
359 if(again == 1 && mface[i].v4)
361 VECSUB(side1, mvert[ mface[i].v3 ].co, mvert[ mface[i].v1 ].co);
362 VECSUB(side2, mvert[ mface[i].v4 ].co, mvert[ mface[i].v1 ].co);
366 VECSUB(side1, mvert[ mface[i].v2 ].co, mvert[ mface[i].v1 ].co);
367 VECSUB(side2, mvert[ mface[i].v3 ].co, mvert[ mface[i].v1 ].co);
370 cross_v3_v3v3(trinormorg, side1, side2);
371 normalize_v3(trinormorg);
372 VECCOPY(trinorm, trinormorg);
373 mul_v3_fl(trinorm, 0.25 * cell_len);
375 for(j = 0; j <= divs1; j++)
377 for(k = 0; k <= divs2; k++)
379 float p1[3], p2[3], p3[3], p[3]={0,0,0};
380 const float uf = (float)(j + TRI_UVOFFSET) / (float)(divs1 + 0.0);
381 const float vf = (float)(k + TRI_UVOFFSET) / (float)(divs2 + 0.0);
386 // printf("bigger - divs1: %d, divs2: %d\n", divs1, divs2);
390 VECCOPY(p1, mvert[ mface[i].v1 ].co);
391 if(again == 1 && mface[i].v4)
393 VECCOPY(p2, mvert[ mface[i].v3 ].co);
394 VECCOPY(p3, mvert[ mface[i].v4 ].co);
398 VECCOPY(p2, mvert[ mface[i].v2 ].co);
399 VECCOPY(p3, mvert[ mface[i].v3 ].co);
402 mul_v3_fl(p1, (1.0-uf-vf));
410 printf("mem problem\n");
412 // mMovPoints.push_back(p + trinorm);
414 VECADD(tmpvec, tmpvec, trinorm);
415 mul_m4_v3(ob->obmat, tmpvec);
416 VECCOPY(&scs->points[3 * (dm->getNumVerts(dm) + newdivs)], tmpvec);
420 printf("mem problem\n");
422 // mMovPoints.push_back(p - trinorm);
424 VECSUB(tmpvec, tmpvec, trinorm);
425 mul_m4_v3(ob->obmat, tmpvec);
426 VECCOPY(&scs->points[3 * (dm->getNumVerts(dm) + newdivs)], tmpvec);
431 if(again == 0 && mface[i].v4)
441 scs->numpoints = dm->getNumVerts(dm) + newdivs;
446 /*! init triangle divisions */
447 void calcTriangleDivs(Object *ob, MVert *verts, int UNUSED(numverts), MFace *faces, int numfaces, int numtris, int **tridivs, float cell_len)
449 // mTriangleDivs1.resize( faces.size() );
450 // mTriangleDivs2.resize( faces.size() );
451 // mTriangleDivs3.resize( faces.size() );
453 size_t i = 0, facecounter = 0;
454 float maxscale[3] = {1,1,1}; // = channelFindMaxVf(mcScale);
455 float maxpart = ABS(maxscale[0]);
458 if(ABS(maxscale[1])>maxpart) maxpart = ABS(maxscale[1]);
459 if(ABS(maxscale[2])>maxpart) maxpart = ABS(maxscale[2]);
460 scaleFac = 1.0 / maxpart;
461 // featureSize = mLevel[mMaxRefine].nodeSize
462 fsTri = cell_len * 0.5 * scaleFac;
467 *tridivs = MEM_callocN(sizeof(int) * numtris * 3, "Smoke_Tridivs");
469 for(i = 0, facecounter = 0; i < numfaces; i++)
471 float p0[3], p1[3], p2[3];
475 int divs1=0, divs2=0, divs3=0;
477 VECCOPY(p0, verts[faces[i].v1].co);
478 mul_m4_v3(ob->obmat, p0);
479 VECCOPY(p1, verts[faces[i].v2].co);
480 mul_m4_v3(ob->obmat, p1);
481 VECCOPY(p2, verts[faces[i].v3].co);
482 mul_m4_v3(ob->obmat, p2);
484 VECSUB(side1, p1, p0);
485 VECSUB(side2, p2, p0);
486 VECSUB(side3, p1, p2);
488 if(INPR(side1, side1) > fsTri*fsTri)
490 float tmp = normalize_v3(side1);
491 divs1 = (int)ceil(tmp/fsTri);
493 if(INPR(side2, side2) > fsTri*fsTri)
495 float tmp = normalize_v3(side2);
496 divs2 = (int)ceil(tmp/fsTri);
501 printf("b tmp: %f, fsTri: %f, divs2: %d\n", tmp, fsTri, divs2);
505 (*tridivs)[3 * facecounter + 0] = divs1;
506 (*tridivs)[3 * facecounter + 1] = divs2;
507 (*tridivs)[3 * facecounter + 2] = divs3;
512 divs1=0, divs2=0, divs3=0;
516 VECCOPY(p0, verts[faces[i].v3].co);
517 mul_m4_v3(ob->obmat, p0);
518 VECCOPY(p1, verts[faces[i].v4].co);
519 mul_m4_v3(ob->obmat, p1);
520 VECCOPY(p2, verts[faces[i].v1].co);
521 mul_m4_v3(ob->obmat, p2);
523 VECSUB(side1, p1, p0);
524 VECSUB(side2, p2, p0);
525 VECSUB(side3, p1, p2);
527 if(INPR(side1, side1) > fsTri*fsTri)
529 float tmp = normalize_v3(side1);
530 divs1 = (int)ceil(tmp/fsTri);
532 if(INPR(side2, side2) > fsTri*fsTri)
534 float tmp = normalize_v3(side2);
535 divs2 = (int)ceil(tmp/fsTri);
538 (*tridivs)[3 * facecounter + 0] = divs1;
539 (*tridivs)[3 * facecounter + 1] = divs2;
540 (*tridivs)[3 * facecounter + 2] = divs3;
546 static void smokeModifier_freeDomain(SmokeModifierData *smd)
550 if(smd->domain->shadow)
551 MEM_freeN(smd->domain->shadow);
552 smd->domain->shadow = NULL;
554 if(smd->domain->fluid)
555 smoke_free(smd->domain->fluid);
558 smoke_turbulence_free(smd->domain->wt);
560 if(smd->domain->effector_weights)
561 MEM_freeN(smd->domain->effector_weights);
562 smd->domain->effector_weights = NULL;
564 BKE_ptcache_free_list(&(smd->domain->ptcaches[0]));
565 smd->domain->point_cache[0] = NULL;
567 MEM_freeN(smd->domain);
572 static void smokeModifier_freeFlow(SmokeModifierData *smd)
579 free_bvhtree_from_mesh(smd->flow->bvh);
580 MEM_freeN(smd->flow->bvh);
582 smd->flow->bvh = NULL;
584 MEM_freeN(smd->flow);
589 static void smokeModifier_freeCollision(SmokeModifierData *smd)
593 if(smd->coll->points)
595 MEM_freeN(smd->coll->points);
596 smd->coll->points = NULL;
599 if(smd->coll->bvhtree)
601 BLI_bvhtree_free(smd->coll->bvhtree);
602 smd->coll->bvhtree = NULL;
606 smd->coll->dm->release(smd->coll->dm);
607 smd->coll->dm = NULL;
609 MEM_freeN(smd->coll);
614 void smokeModifier_reset_turbulence(struct SmokeModifierData *smd)
616 if(smd && smd->domain && smd->domain->wt)
618 smoke_turbulence_free(smd->domain->wt);
619 smd->domain->wt = NULL;
623 void smokeModifier_reset(struct SmokeModifierData *smd)
629 if(smd->domain->shadow)
630 MEM_freeN(smd->domain->shadow);
631 smd->domain->shadow = NULL;
633 if(smd->domain->fluid)
635 smoke_free(smd->domain->fluid);
636 smd->domain->fluid = NULL;
639 smokeModifier_reset_turbulence(smd);
643 // printf("reset domain end\n");
650 free_bvhtree_from_mesh(smd->flow->bvh);
651 MEM_freeN(smd->flow->bvh);
653 smd->flow->bvh = NULL;
658 if(smd->coll->points)
660 MEM_freeN(smd->coll->points);
661 smd->coll->points = NULL;
664 if(smd->coll->bvhtree)
666 BLI_bvhtree_free(smd->coll->bvhtree);
667 smd->coll->bvhtree = NULL;
671 smd->coll->dm->release(smd->coll->dm);
672 smd->coll->dm = NULL;
678 void smokeModifier_free (SmokeModifierData *smd)
682 smokeModifier_freeDomain(smd);
683 smokeModifier_freeFlow(smd);
684 smokeModifier_freeCollision(smd);
688 void smokeModifier_createType(struct SmokeModifierData *smd)
692 if(smd->type & MOD_SMOKE_TYPE_DOMAIN)
695 smokeModifier_freeDomain(smd);
697 smd->domain = MEM_callocN(sizeof(SmokeDomainSettings), "SmokeDomain");
699 smd->domain->smd = smd;
701 smd->domain->point_cache[0] = BKE_ptcache_add(&(smd->domain->ptcaches[0]));
702 smd->domain->point_cache[0]->flag |= PTCACHE_DISK_CACHE;
703 smd->domain->point_cache[0]->step = 1;
706 smd->domain->point_cache[1] = NULL;
707 smd->domain->ptcaches[1].first = smd->domain->ptcaches[1].last = NULL;
708 /* set some standard values */
709 smd->domain->fluid = NULL;
710 smd->domain->wt = NULL;
711 smd->domain->eff_group = NULL;
712 smd->domain->fluid_group = NULL;
713 smd->domain->coll_group = NULL;
714 smd->domain->maxres = 32;
715 smd->domain->amplify = 1;
716 smd->domain->omega = 1.0;
717 smd->domain->alpha = -0.001;
718 smd->domain->beta = 0.1;
719 smd->domain->time_scale = 1.0;
720 smd->domain->vorticity = 2.0;
721 smd->domain->border_collisions = 1; // vertically non-colliding
722 smd->domain->flags = MOD_SMOKE_DISSOLVE_LOG | MOD_SMOKE_HIGH_SMOOTH;
723 smd->domain->strength = 2.0;
724 smd->domain->noise = MOD_SMOKE_NOISEWAVE;
725 smd->domain->diss_speed = 5;
726 // init 3dview buffer
728 smd->domain->viewsettings = MOD_SMOKE_VIEW_SHOWBIG;
729 smd->domain->effector_weights = BKE_add_effector_weights(NULL);
731 else if(smd->type & MOD_SMOKE_TYPE_FLOW)
734 smokeModifier_freeFlow(smd);
736 smd->flow = MEM_callocN(sizeof(SmokeFlowSettings), "SmokeFlow");
738 smd->flow->smd = smd;
740 /* set some standard values */
741 smd->flow->density = 1.0;
742 smd->flow->temp = 1.0;
743 smd->flow->flags = MOD_SMOKE_FLOW_ABSOLUTE;
744 smd->flow->vel_multi = 1.0;
746 smd->flow->psys = NULL;
749 else if(smd->type & MOD_SMOKE_TYPE_COLL)
752 smokeModifier_freeCollision(smd);
754 smd->coll = MEM_callocN(sizeof(SmokeCollSettings), "SmokeColl");
756 smd->coll->smd = smd;
757 smd->coll->points = NULL;
758 smd->coll->numpoints = 0;
759 smd->coll->bvhtree = NULL;
760 smd->coll->dm = NULL;
765 void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData *tsmd)
767 tsmd->type = smd->type;
768 tsmd->time = smd->time;
770 smokeModifier_createType(tsmd);
773 tsmd->domain->maxres = smd->domain->maxres;
774 tsmd->domain->amplify = smd->domain->amplify;
775 tsmd->domain->omega = smd->domain->omega;
776 tsmd->domain->alpha = smd->domain->alpha;
777 tsmd->domain->beta = smd->domain->beta;
778 tsmd->domain->flags = smd->domain->flags;
779 tsmd->domain->strength = smd->domain->strength;
780 tsmd->domain->noise = smd->domain->noise;
781 tsmd->domain->diss_speed = smd->domain->diss_speed;
782 tsmd->domain->viewsettings = smd->domain->viewsettings;
783 tsmd->domain->fluid_group = smd->domain->fluid_group;
784 tsmd->domain->coll_group = smd->domain->coll_group;
785 tsmd->domain->vorticity = smd->domain->vorticity;
786 tsmd->domain->time_scale = smd->domain->time_scale;
787 tsmd->domain->border_collisions = smd->domain->border_collisions;
789 MEM_freeN(tsmd->domain->effector_weights);
790 tsmd->domain->effector_weights = MEM_dupallocN(smd->domain->effector_weights);
791 } else if (tsmd->flow) {
792 tsmd->flow->density = smd->flow->density;
793 tsmd->flow->temp = smd->flow->temp;
794 tsmd->flow->psys = smd->flow->psys;
795 tsmd->flow->type = smd->flow->type;
796 tsmd->flow->flags = smd->flow->flags;
797 tsmd->flow->vel_multi = smd->flow->vel_multi;
798 } else if (tsmd->coll) {
800 /* leave it as initialised, collision settings is mostly caches */
805 // forward decleration
806 static void smoke_calc_transparency(float *result, float *input, float *p0, float *p1, int res[3], float dx, float *light, bresenham_callback cb, float correct);
807 static float calc_voxel_transp(float *result, float *input, int res[3], int *pixel, float *tRay, float correct);
808 static int get_lamp(Scene *scene, float *light)
810 Base *base_tmp = NULL;
813 // try to find a lamp, preferably local
814 for(base_tmp = scene->base.first; base_tmp; base_tmp= base_tmp->next) {
815 if(base_tmp->object->type == OB_LAMP) {
816 Lamp *la = base_tmp->object->data;
818 if(la->type == LA_LOCAL) {
819 copy_v3_v3(light, base_tmp->object->obmat[3]);
822 else if(!found_lamp) {
823 copy_v3_v3(light, base_tmp->object->obmat[3]);
832 static void smoke_calc_domain(Scene *scene, Object *ob, SmokeModifierData *smd)
834 SmokeDomainSettings *sds = smd->domain;
835 GroupObject *go = NULL;
838 // do collisions, needs to be done before emission, so that smoke isn't emitted inside collision cells
841 Object *otherobj = NULL;
842 ModifierData *md = NULL;
844 if(sds->coll_group) // we use groups since we have 2 domains
845 go = sds->coll_group->gobject.first;
847 base = scene->base.first;
858 otherobj = base->object;
867 md = modifiers_findByType(otherobj, eModifierType_Smoke);
869 // check for active smoke modifier
870 if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render))
872 SmokeModifierData *smd2 = (SmokeModifierData *)md;
874 if((smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll && smd2->coll->points)
876 // we got nice collision object
877 SmokeCollSettings *scs = smd2->coll;
879 unsigned char *obstacles = smoke_get_obstacle(smd->domain->fluid);
881 for(i = 0; i < scs->numpoints; i++)
887 // 1. get corresponding cell
888 get_cell(smd->domain->p0, smd->domain->res, smd->domain->dx, &scs->points[3 * i], cell, 0);
890 // check if cell is valid (in the domain boundary)
891 for(j = 0; j < 3; j++)
892 if((cell[j] > sds->res[j] - 1) || (cell[j] < 0))
900 // 2. set cell values (heat, density and velocity)
901 index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]);
903 // printf("cell[0]: %d, cell[1]: %d, cell[2]: %d\n", cell[0], cell[1], cell[2]);
904 // printf("res[0]: %d, res[1]: %d, res[2]: %d, index: %d\n\n", sds->res[0], sds->res[1], sds->res[2], index);
905 obstacles[index] = 1;
906 // for moving gobstacles
908 const LbmFloat maxVelVal = 0.1666;
909 const LbmFloat maxusqr = maxVelVal*maxVelVal*3. *1.5;
911 LbmVec objvel = vec2L((mMOIVertices[n]-mMOIVerticesOld[n]) /dvec);
913 const LbmFloat usqr = (objvel[0]*objvel[0]+objvel[1]*objvel[1]+objvel[2]*objvel[2])*1.5;
914 USQRMAXCHECK(usqr, objvel[0],objvel[1],objvel[2], mMaxVlen, mMxvx,mMxvy,mMxvz);
916 // cutoff at maxVelVal
917 for(int jj=0; jj<3; jj++) {
918 if(objvel[jj]>0.) objvel[jj] = maxVelVal;
919 if(objvel[jj]<0.) objvel[jj] = -maxVelVal;
923 const LbmFloat dp=dot(objvel, vec2L((*pNormals)[n]) );
924 const LbmVec oldov=objvel; // debug
925 objvel = vec2L((*pNormals)[n]) *dp;
938 // do flows and fluids
941 Object *otherobj = NULL;
942 ModifierData *md = NULL;
943 if(sds->fluid_group) // we use groups since we have 2 domains
944 go = sds->fluid_group->gobject.first;
946 base = scene->base.first;
956 otherobj = base->object;
967 md = modifiers_findByType(otherobj, eModifierType_Smoke);
969 // check for active smoke modifier
970 if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render))
972 SmokeModifierData *smd2 = (SmokeModifierData *)md;
974 // check for initialized smoke object
975 if((smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow)
977 // we got nice flow object
978 SmokeFlowSettings *sfs = smd2->flow;
980 if(sfs && sfs->psys && sfs->psys->part && sfs->psys->part->type==PART_EMITTER) // is particle system selected
982 ParticleSimulationData sim;
983 ParticleSystem *psys = sfs->psys;
985 float *density = smoke_get_density(sds->fluid);
986 float *bigdensity = smoke_turbulence_get_density(sds->wt);
987 float *heat = smoke_get_heat(sds->fluid);
988 float *velocity_x = smoke_get_velocity_x(sds->fluid);
989 float *velocity_y = smoke_get_velocity_y(sds->fluid);
990 float *velocity_z = smoke_get_velocity_z(sds->fluid);
991 unsigned char *obstacle = smoke_get_obstacle(sds->fluid);
993 short absolute_flow = (sfs->flags & MOD_SMOKE_FLOW_ABSOLUTE);
994 short high_emission_smoothing = bigdensity ? (smd->domain->flags & MOD_SMOKE_HIGH_SMOOTH) : 0;
997 * A temporary volume map used to store whole emissive
998 * area to be added to smoke density and interpolated
999 * for high resolution smoke.
1001 float *temp_emission_map = NULL;
1007 // initialize temp emission map
1008 if(!(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW))
1011 temp_emission_map = MEM_callocN(sizeof(float) * sds->res[0]*sds->res[1]*sds->res[2], "SmokeTempEmission");
1012 // set whole volume to 0.0f
1013 for (i=0; i<sds->res[0]*sds->res[1]*sds->res[2]; i++) {
1014 temp_emission_map[i] = 0.0f;
1018 // mostly copied from particle code
1019 for(p=0; p<psys->totpart; p++)
1027 if(psys->particles[p].flag & (PARS_NO_DISP|PARS_UNEXIST))
1030 state.time = smd->time;
1032 if(psys_get_particle_state(&sim, p, &state, 0) == 0)
1035 // VECCOPY(pos, pa->state.co);
1036 // mul_m4_v3(ob->imat, pos);
1037 // 1. get corresponding cell
1038 get_cell(smd->domain->p0, smd->domain->res, smd->domain->dx, state.co, cell, 0);
1039 // check if cell is valid (in the domain boundary)
1040 for(i = 0; i < 3; i++)
1042 if((cell[i] > sds->res[i] - 1) || (cell[i] < 0))
1050 // 2. set cell values (heat, density and velocity)
1051 index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]);
1052 if(!(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW) && !(obstacle[index])) // this is inflow
1054 // heat[index] += sfs->temp * 0.1;
1055 // density[index] += sfs->density * 0.1;
1056 heat[index] = sfs->temp;
1058 // Add emitter density to temp emission map
1059 temp_emission_map[index] = sfs->density;
1061 // Uses particle velocity as initial velocity for smoke
1062 if(sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO))
1064 velocity_x[index] = state.vel[0]*sfs->vel_multi;
1065 velocity_y[index] = state.vel[1]*sfs->vel_multi;
1066 velocity_z[index] = state.vel[2]*sfs->vel_multi;
1069 else if(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW) // outflow
1072 density[index] = 0.f;
1073 velocity_x[index] = 0.f;
1074 velocity_y[index] = 0.f;
1075 velocity_z[index] = 0.f;
1076 // we need different handling for the high-res feature
1079 // init all surrounding cells according to amplification, too
1081 smoke_turbulence_get_res(smd->domain->wt, bigres);
1083 for(i = 0; i < smd->domain->amplify + 1; i++)
1084 for(j = 0; j < smd->domain->amplify + 1; j++)
1085 for(k = 0; k < smd->domain->amplify + 1; k++)
1087 index = smoke_get_index((smd->domain->amplify + 1)* cell[0] + i, bigres[0], (smd->domain->amplify + 1)* cell[1] + j, bigres[1], (smd->domain->amplify + 1)* cell[2] + k);
1088 bigdensity[index] = 0.f;
1095 // apply emission values
1096 if(!(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW)) {
1098 // initialize variables
1099 int ii, jj, kk, x, y, z, block_size;
1100 size_t index, index_big;
1102 smoke_turbulence_get_res(smd->domain->wt, bigres);
1103 block_size = smd->domain->amplify + 1; // high res block size
1106 // loop through every low res cell
1107 for(x = 0; x < sds->res[0]; x++)
1108 for(y = 0; y < sds->res[1]; y++)
1109 for(z = 0; z < sds->res[2]; z++)
1112 // neighbour cell emission densities (for high resolution smoke smooth interpolation)
1113 float c000, c001, c010, c011, c100, c101, c110, c111;
1115 c000 = (x>0 && y>0 && z>0) ? temp_emission_map[smoke_get_index(x-1, sds->res[0], y-1, sds->res[1], z-1)] : 0;
1116 c001 = (x>0 && y>0) ? temp_emission_map[smoke_get_index(x-1, sds->res[0], y-1, sds->res[1], z)] : 0;
1117 c010 = (x>0 && z>0) ? temp_emission_map[smoke_get_index(x-1, sds->res[0], y, sds->res[1], z-1)] : 0;
1118 c011 = (x>0) ? temp_emission_map[smoke_get_index(x-1, sds->res[0], y, sds->res[1], z)] : 0;
1120 c100 = (y>0 && z>0) ? temp_emission_map[smoke_get_index(x, sds->res[0], y-1, sds->res[1], z-1)] : 0;
1121 c101 = (y>0) ? temp_emission_map[smoke_get_index(x, sds->res[0], y-1, sds->res[1], z)] : 0;
1122 c110 = (z>0) ? temp_emission_map[smoke_get_index(x, sds->res[0], y, sds->res[1], z-1)] : 0;
1123 c111 = temp_emission_map[smoke_get_index(x, sds->res[0], y, sds->res[1], z)]; // this cell
1128 index = smoke_get_index(x, sds->res[0], y, sds->res[1], z);
1130 // add emission to low resolution density
1131 if (absolute_flow) {if (temp_emission_map[index]>0) density[index] = temp_emission_map[index];}
1133 density[index] += temp_emission_map[index];
1134 if (density[index]>1) density[index]=1.0f;
1137 smoke_turbulence_get_res(smd->domain->wt, bigres);
1142 loop through high res blocks if high res enabled
1145 for(ii = 0; ii < block_size; ii++)
1146 for(jj = 0; jj < block_size; jj++)
1147 for(kk = 0; kk < block_size; kk++)
1150 float fx,fy,fz, interpolated_value;
1151 int shift_x, shift_y, shift_z;
1155 * Do volume interpolation if emitter smoothing
1158 if (high_emission_smoothing) {
1159 // convert block position to relative
1160 // for interpolation smoothing
1161 fx = (float)ii/block_size + 0.5f/block_size;
1162 fy = (float)jj/block_size + 0.5f/block_size;
1163 fz = (float)kk/block_size + 0.5f/block_size;
1165 // calculate trilinear interpolation
1166 interpolated_value = c000 * (1-fx) * (1-fy) * (1-fz) +
1167 c100 * fx * (1-fy) * (1-fz) +
1168 c010 * (1-fx) * fy * (1-fz) +
1169 c001 * (1-fx) * (1-fy) * fz +
1170 c101 * fx * (1-fy) * fz +
1171 c011 * (1-fx) * fy * fz +
1172 c110 * fx * fy * (1-fz) +
1173 c111 * fx * fy * fz;
1176 // add some contrast / sharpness
1177 // depending on hi-res block size
1179 interpolated_value = (interpolated_value-0.4f*sfs->density)*(block_size/2) + 0.4f*sfs->density;
1180 if (interpolated_value<0.0f) interpolated_value = 0.0f;
1181 if (interpolated_value>1.0f) interpolated_value = 1.0f;
1183 // shift smoke block index
1184 // (because pixel center is actually
1185 // in halfway of the low res block)
1186 shift_x = (x < 1) ? 0 : block_size/2;
1187 shift_y = (y < 1) ? 0 : block_size/2;
1188 shift_z = (z < 1) ? 0 : block_size/2;
1191 // without interpolation use same low resolution
1192 // block value for all hi-res blocks
1193 interpolated_value = c111;
1199 // get shifted index for current high resolution block
1200 index_big = smoke_get_index(block_size * x + ii - shift_x, bigres[0], block_size * y + jj - shift_y, bigres[1], block_size * z + kk - shift_z);
1202 // add emission data to high resolution density
1203 if (absolute_flow) {if (interpolated_value > 0) bigdensity[index_big] = interpolated_value;}
1205 bigdensity[index_big] += interpolated_value;
1206 if (bigdensity[index_big]>1) bigdensity[index_big]=1.0f;
1209 } // end of hires loop
1211 } // end of low res loop
1213 // free temporary emission map
1214 if (temp_emission_map) MEM_freeN(temp_emission_map);
1227 BVHTreeNearest nearest;
1229 nearest.dist = FLT_MAX;
1231 BLI_bvhtree_find_nearest(sfs->bvh->tree, pco, &nearest, sfs->bvh->nearest_callback, sfs->bvh);
1236 if(sds->fluid_group)
1245 ListBase *effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights);
1249 float *density = smoke_get_density(sds->fluid);
1250 float *force_x = smoke_get_force_x(sds->fluid);
1251 float *force_y = smoke_get_force_y(sds->fluid);
1252 float *force_z = smoke_get_force_z(sds->fluid);
1253 float *velocity_x = smoke_get_velocity_x(sds->fluid);
1254 float *velocity_y = smoke_get_velocity_y(sds->fluid);
1255 float *velocity_z = smoke_get_velocity_z(sds->fluid);
1258 // precalculate wind forces
1259 for(x = 0; x < sds->res[0]; x++)
1260 for(y = 0; y < sds->res[1]; y++)
1261 for(z = 0; z < sds->res[2]; z++)
1263 EffectedPoint epoint;
1264 float voxelCenter[3] = {0,0,0} , vel[3] = {0,0,0} , retvel[3] = {0,0,0};
1265 unsigned int index = smoke_get_index(x, sds->res[0], y, sds->res[1], z);
1267 if(density[index] < FLT_EPSILON)
1270 vel[0] = velocity_x[index];
1271 vel[1] = velocity_y[index];
1272 vel[2] = velocity_z[index];
1274 voxelCenter[0] = sds->p0[0] + sds->dx * x + sds->dx * 0.5;
1275 voxelCenter[1] = sds->p0[1] + sds->dx * y + sds->dx * 0.5;
1276 voxelCenter[2] = sds->p0[2] + sds->dx * z + sds->dx * 0.5;
1278 pd_point_from_loc(scene, voxelCenter, vel, index, &epoint);
1279 pdDoEffectors(effectors, NULL, sds->effector_weights, &epoint, retvel, NULL);
1281 // TODO dg - do in force!
1282 force_x[index] = MIN2(MAX2(-1.0, retvel[0] * 0.2), 1.0);
1283 force_y[index] = MIN2(MAX2(-1.0, retvel[1] * 0.2), 1.0);
1284 force_z[index] = MIN2(MAX2(-1.0, retvel[2] * 0.2), 1.0);
1288 pdEndEffectors(&effectors);
1292 void smokeModifier_do(SmokeModifierData *smd, Scene *scene, Object *ob, DerivedMesh *dm)
1294 if((smd->type & MOD_SMOKE_TYPE_FLOW))
1296 if(scene->r.cfra >= smd->time)
1297 smokeModifier_init(smd, ob, scene, dm);
1299 if(scene->r.cfra > smd->time)
1302 smd->time = scene->r.cfra;
1304 // rigid movement support
1306 copy_m4_m4(smd->flow->mat_old, smd->flow->mat);
1307 copy_m4_m4(smd->flow->mat, ob->obmat);
1310 else if(scene->r.cfra < smd->time)
1312 smd->time = scene->r.cfra;
1313 smokeModifier_reset(smd);
1316 else if(smd->type & MOD_SMOKE_TYPE_COLL)
1318 if(scene->r.cfra >= smd->time)
1319 smokeModifier_init(smd, ob, scene, dm);
1321 if(scene->r.cfra > smd->time)
1324 smd->time = scene->r.cfra;
1327 smd->coll->dm->release(smd->coll->dm);
1329 smd->coll->dm = CDDM_copy(dm);
1331 // rigid movement support
1332 copy_m4_m4(smd->coll->mat_old, smd->coll->mat);
1333 copy_m4_m4(smd->coll->mat, ob->obmat);
1335 else if(scene->r.cfra < smd->time)
1337 smd->time = scene->r.cfra;
1338 smokeModifier_reset(smd);
1341 else if(smd->type & MOD_SMOKE_TYPE_DOMAIN)
1343 SmokeDomainSettings *sds = smd->domain;
1345 PointCache *cache = NULL;
1347 int startframe, endframe, framenr;
1350 framenr = scene->r.cfra;
1352 //printf("time: %d\n", scene->r.cfra);
1354 cache = sds->point_cache[0];
1355 BKE_ptcache_id_from_smoke(&pid, ob, smd);
1356 BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, ×cale);
1358 if(!smd->domain->fluid || framenr == startframe)
1360 BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
1361 BKE_ptcache_validate(cache, framenr);
1362 cache->flag &= ~PTCACHE_REDO_NEEDED;
1365 if(!smd->domain->fluid && (framenr != startframe) && (smd->domain->flags & MOD_SMOKE_FILE_LOAD)==0 && (cache->flag & PTCACHE_BAKED)==0)
1368 smd->domain->flags &= ~MOD_SMOKE_FILE_LOAD;
1370 CLAMP(framenr, startframe, endframe);
1372 /* If already viewing a pre/after frame, no need to reload */
1373 if ((smd->time == framenr) && (framenr != scene->r.cfra))
1376 // printf("startframe: %d, framenr: %d\n", startframe, framenr);
1378 if(smokeModifier_init(smd, ob, scene, dm)==0)
1380 printf("bad smokeModifier_init\n");
1384 /* try to read from cache */
1385 if(BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) {
1386 BKE_ptcache_validate(cache, framenr);
1387 smd->time = framenr;
1391 /* only calculate something when we advanced a single frame */
1392 if(framenr != (int)smd->time+1)
1395 /* don't simulate if viewing start frame, but scene frame is not real start frame */
1396 if (framenr != scene->r.cfra)
1401 smoke_calc_domain(scene, ob, smd);
1403 /* if on second frame, write cache for first frame */
1404 if((int)smd->time == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact==0)) {
1405 // create shadows straight after domain initialization so we get nice shadows for startframe, too
1406 if(get_lamp(scene, light))
1407 smoke_calc_transparency(sds->shadow, smoke_get_density(sds->fluid), sds->p0, sds->p1, sds->res, sds->dx, light, calc_voxel_transp, -7.0*sds->dx);
1411 if(sds->flags & MOD_SMOKE_DISSOLVE)
1412 smoke_dissolve_wavelet(sds->wt, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
1413 smoke_turbulence_step(sds->wt, sds->fluid);
1416 BKE_ptcache_write(&pid, startframe);
1420 smd->time = scene->r.cfra;
1426 // simulate the actual smoke (c++ code in intern/smoke)
1427 // DG: interesting commenting this line + deactivating loading of noise files
1428 if(framenr!=startframe)
1430 if(sds->flags & MOD_SMOKE_DISSOLVE)
1431 smoke_dissolve(sds->fluid, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
1432 smoke_step(sds->fluid, smd->time, scene->r.frs_sec / scene->r.frs_sec_base);
1435 // create shadows before writing cache so they get stored
1436 if(get_lamp(scene, light))
1437 smoke_calc_transparency(sds->shadow, smoke_get_density(sds->fluid), sds->p0, sds->p1, sds->res, sds->dx, light, calc_voxel_transp, -7.0*sds->dx);
1441 if(sds->flags & MOD_SMOKE_DISSOLVE)
1442 smoke_dissolve_wavelet(sds->wt, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
1443 smoke_turbulence_step(sds->wt, sds->fluid);
1446 BKE_ptcache_validate(cache, framenr);
1447 if(framenr != startframe)
1448 BKE_ptcache_write(&pid, framenr);
1451 //printf ( "Frame: %d, Time: %f\n", (int)smd->time, ( float ) tval() );
1455 static float calc_voxel_transp(float *result, float *input, int res[3], int *pixel, float *tRay, float correct)
1457 const size_t index = smoke_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]);
1460 *tRay *= exp(input[index]*correct);
1462 if(result[index] < 0.0f)
1464 #pragma omp critical
1465 result[index] = *tRay;
1471 long long smoke_get_mem_req(int xres, int yres, int zres, int amplify)
1473 int totalCells = xres * yres * zres;
1474 int amplifiedCells = totalCells * amplify * amplify * amplify;
1476 // print out memory requirements
1477 long long int coarseSize = sizeof(float) * totalCells * 22 +
1478 sizeof(unsigned char) * totalCells;
1480 long long int fineSize = sizeof(float) * amplifiedCells * 7 + // big grids
1481 sizeof(float) * totalCells * 8 + // small grids
1482 sizeof(float) * 128 * 128 * 128; // noise tile
1484 long long int totalMB = (coarseSize + fineSize) / (1024 * 1024);
1489 static void bresenham_linie_3D(int x1, int y1, int z1, int x2, int y2, int z2, float *tRay, bresenham_callback cb, float *result, float *input, int res[3], float correct)
1491 int dx, dy, dz, i, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2;
1502 x_inc = (dx < 0) ? -1 : 1;
1504 y_inc = (dy < 0) ? -1 : 1;
1506 z_inc = (dz < 0) ? -1 : 1;
1512 if ((l >= m) && (l >= n)) {
1515 for (i = 0; i < l; i++) {
1516 if(cb(result, input, res, pixel, tRay, correct) <= FLT_EPSILON)
1530 } else if ((m >= l) && (m >= n)) {
1533 for (i = 0; i < m; i++) {
1534 if(cb(result, input, res, pixel, tRay, correct) <= FLT_EPSILON)
1551 for (i = 0; i < n; i++) {
1552 if(cb(result, input, res, pixel, tRay, correct) <= FLT_EPSILON)
1567 cb(result, input, res, pixel, tRay, correct);
1570 static void get_cell(float *p0, int res[3], float dx, float *pos, int *cell, int correct)
1574 VECSUB(tmp, pos, p0);
1575 mul_v3_fl(tmp, 1.0 / dx);
1579 cell[0] = MIN2(res[0] - 1, MAX2(0, (int)floor(tmp[0])));
1580 cell[1] = MIN2(res[1] - 1, MAX2(0, (int)floor(tmp[1])));
1581 cell[2] = MIN2(res[2] - 1, MAX2(0, (int)floor(tmp[2])));
1585 cell[0] = (int)floor(tmp[0]);
1586 cell[1] = (int)floor(tmp[1]);
1587 cell[2] = (int)floor(tmp[2]);
1591 static void smoke_calc_transparency(float *result, float *input, float *p0, float *p1, int res[3], float dx, float *light, bresenham_callback cb, float correct)
1594 int a, z, slabsize=res[0]*res[1], size= res[0]*res[1]*res[2];
1596 for(a=0; a<size; a++)
1608 #pragma omp parallel for schedule(static,1)
1609 for(z = 0; z < res[2]; z++)
1611 size_t index = z*slabsize;
1614 for(y = 0; y < res[1]; y++)
1615 for(x = 0; x < res[0]; x++, index++)
1617 float voxelCenter[3];
1622 if(result[index] >= 0.0f)
1624 voxelCenter[0] = p0[0] + dx * x + dx * 0.5;
1625 voxelCenter[1] = p0[1] + dx * y + dx * 0.5;
1626 voxelCenter[2] = p0[2] + dx * z + dx * 0.5;
1628 // get starting position (in voxel coords)
1629 if(BLI_bvhtree_bb_raycast(bv, light, voxelCenter, pos) > FLT_EPSILON)
1632 get_cell(p0, res, dx, pos, cell, 1);
1637 get_cell(p0, res, dx, light, cell, 1);
1640 bresenham_linie_3D(cell[0], cell[1], cell[2], x, y, z, &tRay, cb, result, input, res, correct);
1642 // convention -> from a RGBA float array, use G value for tRay
1643 // #pragma omp critical
1644 result[index] = tRay;