New: Editable cache. Fixed: Don't clear cache on e.g. lamp moving. Changed: Switched...
[blender.git] / source / blender / render / intern / source / strand.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: none of this file.
24  *
25  * Contributors: Brecht Van Lommel.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <math.h>
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_key_types.h"
37 #include "DNA_material_types.h"
38
39 #include "BLI_arithb.h"
40 #include "BLI_blenlib.h"
41 #include "BLI_ghash.h"
42 #include "BLI_memarena.h"
43
44 #include "BKE_key.h"
45 #include "BKE_utildefines.h"
46
47 #include "render_types.h"
48 #include "initrender.h"
49 #include "rendercore.h"
50 #include "renderdatabase.h"
51 #include "renderpipeline.h"
52 #include "pixelblending.h"
53 #include "shading.h"
54 #include "strand.h"
55 #include "zbuf.h"
56
57 /* to be removed */
58 void merge_transp_passes(RenderLayer *rl, ShadeResult *shr);
59 void add_transp_passes(RenderLayer *rl, int offset, ShadeResult *shr, float alpha);
60 void hoco_to_zco(ZSpan *zspan, float *zco, float *hoco);
61 void zspan_scanconvert_strand(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float, float) );
62 void zbufsinglewire(ZSpan *zspan, int obi, int zvlnr, float *ho1, float *ho2);
63 int addtosamp_shr(ShadeResult *samp_shr, ShadeSample *ssamp, int addpassflag);
64 void add_transp_speed(RenderLayer *rl, int offset, float *speed, float alpha, long *rdrect);
65 void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf);
66
67 /* *************** */
68
69 #define BUCKETPRIMS_SIZE 256
70
71 typedef struct BucketPrims {
72         struct BucketPrims *next, *prev;
73         void *prim[BUCKETPRIMS_SIZE];
74         int totprim;
75 } BucketPrims;
76
77 typedef struct RenderBuckets {
78         ListBase all;
79         ListBase *inside;
80         ListBase *overlap;
81         int x, y;
82         float insize[2];
83         float zmulx, zmuly, zofsx, zofsy;
84 } RenderBuckets;
85
86 static void add_bucket_prim(ListBase *lb, void *prim)
87 {
88         BucketPrims *bpr= lb->last;
89
90         if(!bpr || bpr->totprim == BUCKETPRIMS_SIZE) {
91                 bpr= MEM_callocN(sizeof(BucketPrims), "BucketPrims");
92                 BLI_addtail(lb, bpr);
93         }
94
95         bpr->prim[bpr->totprim++]= prim;
96 }
97
98 RenderBuckets *init_buckets(Render *re)
99 {
100         RenderBuckets *buckets;
101         RenderPart *pa;
102         float scalex, scaley, cropx, cropy;
103         int x, y, tempparts= 0;
104
105         buckets= MEM_callocN(sizeof(RenderBuckets), "RenderBuckets");
106
107         if(!re->parts.first) {
108                 initparts(re);
109                 tempparts= 1;
110         }
111
112         pa= re->parts.first;
113         if(!pa)
114                 return buckets;
115         
116         x= re->xparts+1;
117         y= re->yparts+1;
118         buckets->x= x;
119         buckets->y= y;
120
121         scalex= (2.0f - re->xparts*re->partx/(float)re->winx);
122         scaley= (2.0f - re->yparts*re->party/(float)re->winy);
123
124         cropx= pa->crop/(float)re->partx;
125         cropy= pa->crop/(float)re->party;
126
127         buckets->insize[0]= 1.0f - 2.0f*cropx;
128         buckets->insize[1]= 1.0f - 2.0f*cropy;
129
130         buckets->zmulx= re->xparts*scalex;
131         buckets->zmuly= re->yparts*scaley;
132         buckets->zofsx= scalex*(1.0f - cropx);
133         buckets->zofsy= scaley*(1.0f - cropy);
134
135         buckets->inside= MEM_callocN(sizeof(ListBase)*x*y, "BucketPrimsInside");
136         buckets->overlap= MEM_callocN(sizeof(ListBase)*x*y, "BucketPrimsOverlap");
137
138         if(tempparts)
139                 freeparts(re);
140
141         return buckets;
142 }
143
144 void add_buckets_primitive(RenderBuckets *buckets, float *min, float *max, void *prim)
145 {
146         float end[3];
147         int x, y, a;
148
149         x= (int)min[0];
150         y= (int)min[1];
151
152         if(x >= 0 && x < buckets->x && y >= 0 && y < buckets->y) {
153                 a= y*buckets->x + x;
154
155                 end[0]= x + buckets->insize[0];
156                 end[1]= y + buckets->insize[1];
157
158                 if(max[0] <= end[0] && max[1] <= end[1]) {
159                         add_bucket_prim(&buckets->inside[a], prim);
160                         return;
161                 }
162                 else {
163                         end[0]= x + 2;
164                         end[1]= y + 2;
165
166                         if(max[0] <= end[0] && max[1] <= end[1]) {
167                                 add_bucket_prim(&buckets->overlap[a], prim);
168                                 return;
169                         }
170                 }
171         }
172
173         add_bucket_prim(&buckets->all, prim);
174 }
175
176 void free_buckets(RenderBuckets *buckets)
177 {
178         int a, size;
179
180         BLI_freelistN(&buckets->all);
181
182         size= buckets->x*buckets->y;
183         for(a=0; a<size; a++) {
184                 BLI_freelistN(&buckets->inside[a]);
185                 BLI_freelistN(&buckets->overlap[a]);
186         }
187
188         if(buckets->inside)
189                 MEM_freeN(buckets->inside);
190         if(buckets->overlap)
191                 MEM_freeN(buckets->overlap);
192         
193         MEM_freeN(buckets);
194 }
195
196 void project_hoco_to_bucket(RenderBuckets *buckets, float *hoco, float *bucketco)
197 {
198         float div;
199
200         div= 1.0f/hoco[3];
201         bucketco[0]= buckets->zmulx*(0.5 + 0.5f*hoco[0]*div) + buckets->zofsx;
202         bucketco[1]= buckets->zmuly*(0.5 + 0.5f*hoco[1]*div) + buckets->zofsy;
203 }
204
205 typedef struct RenderPrimitiveIterator {
206         Render *re;
207         RenderBuckets *buckets;
208         ListBase *list[6];
209         int listindex, totlist;
210         BucketPrims *bpr;
211         int bprindex;
212
213         ObjectInstanceRen *obi;
214         StrandRen *strand;
215         int index, tot;
216 } RenderPrimitiveIterator;
217
218 RenderPrimitiveIterator *init_primitive_iterator(Render *re, RenderBuckets *buckets, RenderPart *pa)
219 {
220         RenderPrimitiveIterator *iter;
221         int nr, x, y, width;
222
223         iter= MEM_callocN(sizeof(RenderPrimitiveIterator), "RenderPrimitiveIterator");
224         iter->re= re;
225
226         if(buckets) {
227                 iter->buckets= buckets;
228
229                 nr= BLI_findindex(&re->parts, pa);
230                 width= buckets->x - 1;
231                 x= (nr % width) + 1;
232                 y= (nr / width) + 1;
233
234                 iter->list[iter->totlist++]= &buckets->all;
235                 iter->list[iter->totlist++]= &buckets->inside[y*buckets->x + x];
236                 iter->list[iter->totlist++]= &buckets->overlap[y*buckets->x + x];
237                 iter->list[iter->totlist++]= &buckets->overlap[y*buckets->x + (x-1)];
238                 iter->list[iter->totlist++]= &buckets->overlap[(y-1)*buckets->x + (x-1)];
239                 iter->list[iter->totlist++]= &buckets->overlap[(y-1)*buckets->x + x];
240         }
241         else {
242                 iter->index= 0;
243                 iter->obi= re->instancetable.first;
244                 if(iter->obi)
245                         iter->tot= iter->obi->obr->totstrand;
246         }
247
248         return iter;
249 }
250
251 void *next_primitive_iterator(RenderPrimitiveIterator *iter)
252 {
253         if(iter->buckets) {
254                 if(iter->bpr && iter->bprindex >= iter->bpr->totprim) {
255                         iter->bpr= iter->bpr->next;
256                         iter->bprindex= 0;
257                 }
258
259                 while(iter->bpr == NULL) {
260                         if(iter->listindex == iter->totlist)
261                                 return NULL;
262
263                         iter->bpr= iter->list[iter->listindex++]->first;
264                         iter->bprindex= 0;
265                 }
266
267                 return iter->bpr->prim[iter->bprindex++];
268         }
269         else {
270                 if(!iter->obi)
271                         return NULL;
272
273                 if(iter->index >= iter->tot) {
274                         while((iter->obi=iter->obi->next) && !iter->obi->obr->totstrand)
275                                 iter->obi= iter->obi->next;
276
277                         if(iter->obi)
278                                 iter->tot= iter->obi->obr->totstrand;
279                         else
280                                 return NULL;
281                 }
282
283                 if(iter->index < iter->tot) {
284                         if((iter->index & 255)==0)
285                                 iter->strand= iter->obi->obr->strandnodes[iter->index>>8].strand;
286                         else
287                                 iter->strand++;
288
289                         return iter->strand;
290                 }
291                 else
292                         return NULL;
293         }
294 }
295
296 void free_primitive_iterator(RenderPrimitiveIterator *iter)
297 {
298         MEM_freeN(iter);
299 }
300
301 /* *************** */
302
303 static float strand_eval_width(Material *ma, float strandco)
304 {
305         float fac;
306
307         strandco= 0.5f*(strandco + 1.0f);
308
309         if(ma->strand_ease!=0.0f) {
310                 if(ma->strand_ease<0.0f)
311                         fac= pow(strandco, 1.0+ma->strand_ease);
312                 else
313                         fac= pow(strandco, 1.0/(1.0f-ma->strand_ease));
314         }
315         else fac= strandco;
316         
317         return ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end);
318 }
319
320 void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint)
321 {
322         Material *ma;
323         StrandBuffer *strandbuf;
324         float *simplify;
325         float p[4][3], data[4], cross[3], crosslen, w, dx, dy, t;
326         int type;
327
328         strandbuf= sseg->buffer;
329         ma= sseg->buffer->ma;
330         t= spoint->t;
331         type= (strandbuf->flag & R_STRAND_BSPLINE)? KEY_BSPLINE: KEY_CARDINAL;
332
333         VECCOPY(p[0], sseg->v[0]->co);
334         VECCOPY(p[1], sseg->v[1]->co);
335         VECCOPY(p[2], sseg->v[2]->co);
336         VECCOPY(p[3], sseg->v[3]->co);
337
338         if(sseg->obi->flag & R_TRANSFORMED) {
339                 Mat4MulVecfl(sseg->obi->mat, p[0]);
340                 Mat4MulVecfl(sseg->obi->mat, p[1]);
341                 Mat4MulVecfl(sseg->obi->mat, p[2]);
342                 Mat4MulVecfl(sseg->obi->mat, p[3]);
343         }
344
345         if(t == 0.0f) {
346                 VECCOPY(spoint->co, p[1]);
347                 spoint->strandco= sseg->v[1]->strandco;
348
349                 spoint->dtstrandco= (sseg->v[2]->strandco - sseg->v[0]->strandco);
350                 if(sseg->v[0] != sseg->v[1])
351                         spoint->dtstrandco *= 0.5f;
352         }
353         else if(t == 1.0f) {
354                 VECCOPY(spoint->co, p[2]);
355                 spoint->strandco= sseg->v[2]->strandco;
356
357                 spoint->dtstrandco= (sseg->v[3]->strandco - sseg->v[1]->strandco);
358                 if(sseg->v[3] != sseg->v[2])
359                         spoint->dtstrandco *= 0.5f;
360         }
361         else {
362                 set_four_ipo(t, data, type);
363                 spoint->co[0]= data[0]*p[0][0] + data[1]*p[1][0] + data[2]*p[2][0] + data[3]*p[3][0];
364                 spoint->co[1]= data[0]*p[0][1] + data[1]*p[1][1] + data[2]*p[2][1] + data[3]*p[3][1];
365                 spoint->co[2]= data[0]*p[0][2] + data[1]*p[1][2] + data[2]*p[2][2] + data[3]*p[3][2];
366                 spoint->strandco= (1.0f-t)*sseg->v[1]->strandco + t*sseg->v[2]->strandco;
367         }
368
369         set_afgeleide_four_ipo(t, data, type);
370         spoint->dtco[0]= data[0]*p[0][0] + data[1]*p[1][0] + data[2]*p[2][0] + data[3]*p[3][0];
371         spoint->dtco[1]= data[0]*p[0][1] + data[1]*p[1][1] + data[2]*p[2][1] + data[3]*p[3][1];
372         spoint->dtco[2]= data[0]*p[0][2] + data[1]*p[1][2] + data[2]*p[2][2] + data[3]*p[3][2];
373
374         VECCOPY(spoint->tan, spoint->dtco);
375         Normalize(spoint->tan);
376
377         VECCOPY(spoint->nor, spoint->co);
378         VECMUL(spoint->nor, -1.0f);
379         Normalize(spoint->nor);
380
381         spoint->width= strand_eval_width(ma, spoint->strandco);
382         
383         /* simplification */
384         simplify= RE_strandren_get_simplify(strandbuf->obr, sseg->strand, 0);
385         spoint->alpha= (simplify)? simplify[1]: 1.0f;
386
387         /* outer points */
388         Crossf(cross, spoint->co, spoint->tan);
389
390         w= spoint->co[2]*strandbuf->winmat[2][3] + strandbuf->winmat[3][3];
391         dx= strandbuf->winx*cross[0]*strandbuf->winmat[0][0]/w;
392         dy= strandbuf->winy*cross[1]*strandbuf->winmat[1][1]/w;
393         w= sqrt(dx*dx + dy*dy);
394
395         if(w > 0.0f) {
396                 if(strandbuf->flag & R_STRAND_B_UNITS) {
397                         crosslen= VecLength(cross);
398                         w= 2.0f*crosslen*strandbuf->minwidth/w;
399
400                         if(spoint->width < w) {
401                                 spoint->alpha= spoint->width/w;
402                                 spoint->width= w;
403                         }
404
405                         if(simplify)
406                                 /* squared because we only change width, not length */
407                                 spoint->width *= simplify[0]*simplify[0];
408
409                         VecMulf(cross, spoint->width*0.5f/crosslen);
410                 }
411                 else
412                         VecMulf(cross, spoint->width/w);
413         }
414
415         VecSubf(spoint->co1, spoint->co, cross);
416         VecAddf(spoint->co2, spoint->co, cross);
417
418         VECCOPY(spoint->dsco, cross);
419 }
420
421 /* *************** */
422
423 typedef struct StrandPart {
424         Render *re;
425         ZSpan *zspan;
426
427         RenderLayer *rl;
428         ShadeResult *result;
429         float *pass;
430         int *rectz, *outrectz;
431         unsigned short *mask;
432         int rectx, recty;
433         int addpassflag, addzbuf, sample;
434
435         StrandSegment *segment;
436         GHash *hash;
437         StrandPoint point1, point2;
438         ShadeSample ssamp1, ssamp2, ssamp;
439         float t[3], s[3];
440 } StrandPart;
441
442 typedef struct StrandSortSegment {
443         struct StrandSortSegment *next;
444         int obi, strand, segment;
445         float z;
446 } StrandSortSegment;
447
448 static int compare_strand_segment(const void *poin1, const void *poin2)
449 {
450         const StrandSortSegment *seg1= (const StrandSortSegment*)poin1;
451         const StrandSortSegment *seg2= (const StrandSortSegment*)poin2;
452
453         if(seg1->z < seg2->z)
454                 return -1;
455         else if(seg1->z == seg2->z)
456                 return 0;
457         else
458                 return 1;
459 }
460
461 static void interpolate_vec1(float *v1, float *v2, float t, float negt, float *v)
462 {
463         v[0]= negt*v1[0] + t*v2[0];
464 }
465
466 static void interpolate_vec3(float *v1, float *v2, float t, float negt, float *v)
467 {
468         v[0]= negt*v1[0] + t*v2[0];
469         v[1]= negt*v1[1] + t*v2[1];
470         v[2]= negt*v1[2] + t*v2[2];
471 }
472
473 static void interpolate_vec4(float *v1, float *v2, float t, float negt, float *v)
474 {
475         v[0]= negt*v1[0] + t*v2[0];
476         v[1]= negt*v1[1] + t*v2[1];
477         v[2]= negt*v1[2] + t*v2[2];
478         v[3]= negt*v1[3] + t*v2[3];
479 }
480
481 static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float t, ShadeResult *shr, int addpassflag)
482 {
483         float negt= 1.0f - t;
484
485         interpolate_vec4(shr1->combined, shr2->combined, t, negt, shr->combined);
486
487         if(addpassflag & SCE_PASS_VECTOR) {
488                 interpolate_vec4(shr1->winspeed, shr2->winspeed, t, negt, shr->winspeed);
489         }
490         /* optim... */
491         if(addpassflag & ~(SCE_PASS_VECTOR)) {
492                 if(addpassflag & SCE_PASS_RGBA)
493                         interpolate_vec4(shr1->col, shr2->col, t, negt, shr->col);
494                 if(addpassflag & SCE_PASS_NORMAL) {
495                         interpolate_vec3(shr1->nor, shr2->nor, t, negt, shr->nor);
496                         Normalize(shr->nor);
497                 }
498                 if(addpassflag & SCE_PASS_DIFFUSE)
499                         interpolate_vec3(shr1->diff, shr2->diff, t, negt, shr->diff);
500                 if(addpassflag & SCE_PASS_SPEC)
501                         interpolate_vec3(shr1->spec, shr2->spec, t, negt, shr->spec);
502                 if(addpassflag & SCE_PASS_SHADOW)
503                         interpolate_vec3(shr1->shad, shr2->shad, t, negt, shr->shad);
504                 if(addpassflag & SCE_PASS_AO)
505                         interpolate_vec3(shr1->ao, shr2->ao, t, negt, shr->ao);
506                 if(addpassflag & SCE_PASS_REFLECT)
507                         interpolate_vec3(shr1->refl, shr2->refl, t, negt, shr->refl);
508                 if(addpassflag & SCE_PASS_REFRACT)
509                         interpolate_vec3(shr1->refr, shr2->refr, t, negt, shr->refr);
510                 if(addpassflag & SCE_PASS_RADIO)
511                         interpolate_vec3(shr1->rad, shr2->rad, t, negt, shr->rad);
512                 if(addpassflag & SCE_PASS_MIST)
513                         interpolate_vec1(&shr1->mist, &shr2->mist, t, negt, &shr->mist);
514         }
515 }
516
517 static void add_strand_obindex(RenderLayer *rl, int offset, ObjectRen *obr)
518 {
519         RenderPass *rpass;
520         
521         for(rpass= rl->passes.first; rpass; rpass= rpass->next) {
522                 if(rpass->passtype == SCE_PASS_INDEXOB) {
523                         float *fp= rpass->rect + offset;
524                         *fp= (float)obr->ob->index;
525                         break;
526                 }
527         }
528 }
529
530 static void strand_apply_shaderesult_alpha(ShadeResult *shr, float alpha)
531 {
532         if(alpha < 1.0f) {
533                 shr->combined[0] *= alpha;
534                 shr->combined[1] *= alpha;
535                 shr->combined[2] *= alpha;
536                 shr->combined[3] *= alpha;
537
538                 shr->col[0] *= alpha;
539                 shr->col[1] *= alpha;
540                 shr->col[2] *= alpha;
541                 shr->col[3] *= alpha;
542
543                 shr->alpha *= alpha;
544         }
545 }
546
547 static void do_strand_point_project(float winmat[][4], ZSpan *zspan, float *co, float *hoco, float *zco)
548 {
549         projectvert(co, winmat, hoco);
550         hoco_to_zco(zspan, zco, hoco);
551 }
552
553 static void strand_project_point(float winmat[][4], float winx, float winy, StrandPoint *spoint)
554 {
555         float div;
556
557         projectvert(spoint->co, winmat, spoint->hoco);
558
559         div= 1.0f/spoint->hoco[3];
560         spoint->x= spoint->hoco[0]*div*winx*0.5f;
561         spoint->y= spoint->hoco[1]*div*winy*0.5f;
562 }
563
564 static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint);
565
566 static void strand_shade_get(StrandPart *spart, int lookup, ShadeSample *ssamp, StrandPoint *spoint, StrandVert *svert, StrandSegment *sseg)
567 {
568         ShadeResult *hashshr;
569
570         if(lookup) {
571                 hashshr= BLI_ghash_lookup(spart->hash, svert);
572
573                 if(!hashshr) {
574                         strand_shade_point(spart->re, ssamp, sseg, spoint);
575
576                         hashshr= MEM_callocN(sizeof(ShadeResult), "HashShadeResult");
577                         *hashshr= ssamp->shr[0];
578                         BLI_ghash_insert(spart->hash, svert, hashshr);
579                 }
580                 else {
581                         ssamp->shr[0]= *hashshr;
582                         BLI_ghash_remove(spart->hash, svert, NULL, (GHashValFreeFP)MEM_freeN);
583                 }
584         }
585         else
586                 strand_shade_point(spart->re, ssamp, sseg, spoint);
587 }
588
589 static void strand_shade_segment(StrandPart *spart)
590 {
591         StrandSegment *sseg= spart->segment;
592         int first, last;
593
594         if(!sseg->shaded) {
595                 first= (sseg->v[1] == &sseg->strand->vert[0]);
596                 last= (sseg->v[2] == &sseg->strand->vert[sseg->strand->totvert-1]);
597
598                 strand_shade_get(spart, !first, &spart->ssamp1, &sseg->point1, sseg->v[1], sseg);
599                 strand_shade_get(spart, !last, &spart->ssamp2, &sseg->point2, sseg->v[2], sseg);
600                 sseg->shaded= 1;
601         }
602 }
603
604 static void do_strand_blend(void *handle, int x, int y, float u, float v, float z)
605 {
606         StrandPart *spart= (StrandPart*)handle;
607         StrandBuffer *buffer= spart->segment->buffer;
608         ShadeResult *shr;
609         float /**pass,*/ t, s;
610         int offset, zverg;
611
612         /* check again solid z-buffer */
613         offset = y*spart->rectx + x;
614         zverg= (int)z;
615
616         if(zverg < spart->rectz[offset]) {
617                 /* fill in output z-buffer if needed */
618                 if(spart->addzbuf)
619                         if(zverg < spart->outrectz[offset])
620                                 spart->outrectz[offset]= zverg;
621
622                 /* check alpha limit */
623                 shr= spart->result + offset*(spart->re->osa? spart->re->osa: 1);
624                 if(shr[spart->sample].combined[3]>0.999f)
625                         return;
626
627                 /* shade points if not shaded yet */
628                 strand_shade_segment(spart);
629
630                 /* interpolate shading from two control points */
631                 t = u*spart->t[0] + v*spart->t[1] + (1.0f-u-v)*spart->t[2];
632                 interpolate_shade_result(spart->ssamp1.shr, spart->ssamp2.shr, t,
633                         spart->ssamp.shr, spart->addpassflag);
634
635                 /* alpha along width */
636                 if(buffer->widthfade != 0.0f) {
637                         s = fabs(u*spart->s[0] + v*spart->s[1] + (1.0f-u-v)*spart->s[2]);
638                         s = 1.0f - pow(s, buffer->widthfade);
639
640                         strand_apply_shaderesult_alpha(spart->ssamp.shr, s);
641                 }
642
643                 /* add in shaderesult array for part */
644                 spart->ssamp.shi[0].mask= (1<<spart->sample);
645                 addtosamp_shr(shr, &spart->ssamp, spart->addpassflag);
646                 spart->mask[offset] |= (1<<spart->sample);
647
648 #if 0
649                 /* fill in pass for preview */
650                 if(spart->sample == 0) {
651                         pass= spart->pass + offset*4;
652                         QUATCOPY(pass, shr->combined);
653                 }
654 #endif
655
656                 if(spart->addpassflag & SCE_PASS_INDEXOB)
657                         add_strand_obindex(spart->rl, offset, buffer->obr);
658         }
659 }
660
661 static int strand_test_clip(float winmat[][4], ZSpan *zspan, float *bounds, float *co, float *zcomp)
662 {
663         float hoco[4];
664         int clipflag= 0;
665
666         projectvert(co, winmat, hoco);
667
668         /* we compare z without perspective division for segment sorting */
669         *zcomp= hoco[2];
670
671         if(hoco[0] > bounds[1]*hoco[3]) clipflag |= 1;
672         else if(hoco[0]< bounds[0]*hoco[3]) clipflag |= 2;
673         else if(hoco[1] > bounds[3]*hoco[3]) clipflag |= 4;
674         else if(hoco[1]< bounds[2]*hoco[3]) clipflag |= 8;
675
676         clipflag |= testclip(hoco);
677
678         return clipflag;
679 }
680
681 static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint)
682 {
683         ShadeInput *shi= ssamp->shi;
684         ShadeResult *shr= ssamp->shr;
685         VlakRen vlr;
686
687         memset(&vlr, 0, sizeof(vlr));
688         vlr.flag= R_SMOOTH;
689         vlr.lay= sseg->strand->buffer->lay;
690         if(sseg->buffer->ma->mode & MA_TANGENT_STR)
691                 vlr.flag |= R_TANGENT;
692
693         shi->vlr= &vlr;
694         shi->strand= sseg->strand;
695         shi->obi= sseg->obi;
696         shi->obr= sseg->obi->obr;
697
698         /* cache for shadow */
699         shi->samplenr++;
700
701         shade_input_set_strand(shi, sseg->strand, spoint);
702         shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint);
703         
704         /* init material vars */
705         // note, keep this synced with render_types.h
706         memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
707         shi->har= shi->mat->har;
708         
709         /* shade */
710         shade_samples_do_AO(ssamp);
711         shade_input_do_shade(shi, shr);
712
713         /* apply simplification */
714         strand_apply_shaderesult_alpha(shr, spoint->alpha);
715
716         /* include lamphalos for strand, since halo layer was added already */
717         if(re->flag & R_LAMPHALO)
718                 if(shi->layflag & SCE_LAY_HALO)
719                         renderspothalo(shi, shr->combined, shr->combined[3]);
720 }
721
722 static void do_scanconvert_strand(Render *re, StrandPart *spart, ZSpan *zspan, float t, float dt, float *co1, float *co2, float *co3, float *co4, int sample)
723 {
724         float jco1[3], jco2[3], jco3[3], jco4[3], jx, jy;
725
726         VECCOPY(jco1, co1);
727         VECCOPY(jco2, co2);
728         VECCOPY(jco3, co3);
729         VECCOPY(jco4, co4);
730
731         if(re->osa) {
732                 jx= -re->jit[sample][0];
733                 jy= -re->jit[sample][1];
734
735                 jco1[0] += jx; jco1[1] += jy;
736                 jco2[0] += jx; jco2[1] += jy;
737                 jco3[0] += jx; jco3[1] += jy;
738                 jco4[0] += jx; jco4[1] += jy;
739         }
740         else if(re->i.curblur) {
741                 jx= -re->jit[re->i.curblur-1][0];
742                 jy= -re->jit[re->i.curblur-1][1];
743
744                 jco1[0] += jx; jco1[1] += jy;
745                 jco2[0] += jx; jco2[1] += jy;
746                 jco3[0] += jx; jco3[1] += jy;
747                 jco4[0] += jx; jco4[1] += jy;
748         }
749
750         spart->sample= sample;
751
752         spart->t[0]= t-dt;
753         spart->s[0]= -1.0f;
754         spart->t[1]= t-dt;
755         spart->s[1]= 1.0f;
756         spart->t[2]= t;
757         spart->s[2]= 1.0f;
758         zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_blend);
759         spart->t[0]= t-dt;
760         spart->s[0]= -1.0f;
761         spart->t[1]= t;
762         spart->s[1]= 1.0f;
763         spart->t[2]= t;
764         spart->s[2]= -1.0f;
765         zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_blend);
766 }
767
768 static void strand_render(Render *re, StrandSegment *sseg, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandPoint *p1, StrandPoint *p2)
769 {
770         if(spart) {
771                 float t= p2->t;
772                 float dt= p2->t - p1->t;
773                 int a;
774
775                 if(re->osa) {
776                         for(a=0; a<re->osa; a++)
777                                 do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, a);
778                 }
779                 else
780                         do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, 0);
781         }
782         else {
783                 float hoco1[4], hoco2[3];
784                 int a, obi, index;
785   
786                 obi= sseg->obi - re->objectinstance;
787                 index= sseg->strand->index;
788
789                 projectvert(p1->co, winmat, hoco1);
790                 projectvert(p2->co, winmat, hoco2);
791   
792                 for(a=0; a<totzspan; a++) {
793 #if 0
794                         /* render both strand and single pixel wire to counter aliasing */
795                         zbufclip4(re, &zspan[a], obi, index, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, p1->clip2, p1->clip1, p2->clip1, p2->clip2);
796 #endif
797                         /* only render a line for now, which makes the shadow map more
798                            similiar across frames, and so reduces flicker */
799                         zbufsinglewire(&zspan[a], obi, index, hoco1, hoco2);
800                 }
801         }
802 }
803   
804 static int strand_segment_recursive(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth)
805 {
806         StrandPoint p;
807         StrandBuffer *buffer= sseg->buffer;
808         float dot, d1[2], d2[2], len1, len2;
809
810         if(depth == buffer->maxdepth)
811                 return 0;
812
813         p.t= (p1->t + p2->t)*0.5f;
814         strand_eval_point(sseg, &p);
815         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, &p);
816
817         d1[0]= (p.x - p1->x);
818         d1[1]= (p.y - p1->y);
819         len1= d1[0]*d1[0] + d1[1]*d1[1];
820
821         d2[0]= (p2->x - p.x);
822         d2[1]= (p2->y - p.y);
823         len2= d2[0]*d2[0] + d2[1]*d2[1];
824
825         if(len1 == 0.0f || len2 == 0.0f)
826                 return 0;
827         
828         dot= d1[0]*d2[0] + d1[1]*d2[1];
829         if(dot*dot > sseg->sqadaptcos*len1*len2)
830                 return 0;
831
832         if(spart) {
833                 do_strand_point_project(winmat, zspan, p.co1, p.hoco1, p.zco1);
834                 do_strand_point_project(winmat, zspan, p.co2, p.hoco2, p.zco2);
835         }
836         else {
837 #if 0
838                 projectvert(p.co1, winmat, p.hoco1);
839                 projectvert(p.co2, winmat, p.hoco2);
840                 p.clip1= testclip(p.hoco1);
841                 p.clip2= testclip(p.hoco2);
842 #endif
843         }
844
845         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, &p, depth+1))
846                 strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, &p);
847         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, &p, p2, depth+1))
848                 strand_render(re, sseg, winmat, spart, zspan, totzspan, &p, p2);
849         
850         return 1;
851 }
852
853 void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg)
854 {
855         StrandBuffer *buffer= sseg->buffer;
856         StrandPoint *p1= &sseg->point1;
857         StrandPoint *p2= &sseg->point2;
858
859         p1->t= 0.0f;
860         p2->t= 1.0f;
861
862         strand_eval_point(sseg, p1);
863         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p1);
864         strand_eval_point(sseg, p2);
865         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p2);
866
867         if(spart) {
868                 do_strand_point_project(winmat, zspan, p1->co1, p1->hoco1, p1->zco1);
869                 do_strand_point_project(winmat, zspan, p1->co2, p1->hoco2, p1->zco2);
870                 do_strand_point_project(winmat, zspan, p2->co1, p2->hoco1, p2->zco1);
871                 do_strand_point_project(winmat, zspan, p2->co2, p2->hoco2, p2->zco2);
872         }
873         else {
874 #if 0
875                 projectvert(p1->co1, winmat, p1->hoco1);
876                 projectvert(p1->co2, winmat, p1->hoco2);
877                 projectvert(p2->co1, winmat, p2->hoco1);
878                 projectvert(p2->co2, winmat, p2->hoco2);
879                 p1->clip1= testclip(p1->hoco1);
880                 p1->clip2= testclip(p1->hoco2);
881                 p2->clip1= testclip(p2->hoco1);
882                 p2->clip2= testclip(p2->hoco2);
883 #endif
884         }
885
886         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, p2, 0))
887                 strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, p2);
888 }
889
890 static void zbuffer_strands_filter(Render *re, RenderPart *pa, RenderLayer *rl, StrandPart *spart, float *pass)
891 {
892         RenderResult *rr= pa->result;
893         ShadeResult *shr, *shrrect= spart->result;
894         float *passrect= pass;
895         long *rdrect;
896         int osa, x, y, a, crop= 0, offs=0, od;
897
898         osa= (re->osa? re->osa: 1);
899
900         /* filtered render, for now we assume only 1 filter size */
901         if(pa->crop) {
902                 crop= 1;
903                 offs= pa->rectx + 1;
904                 passrect+= 4*offs;
905                 shrrect+= offs*osa;
906         }
907
908         rdrect= pa->rectdaps;
909
910         /* zero alpha pixels get speed vector max again */
911         if(spart->addpassflag & SCE_PASS_VECTOR)
912                 if(rl->layflag & SCE_LAY_SOLID)
913                         reset_sky_speedvectors(pa, rl, rl->scolrect);
914
915         /* init scanline updates */
916         rr->renrect.ymin= 0;
917         rr->renrect.ymax= -pa->crop;
918         rr->renlay= rl;
919         
920         /* filter the shade results */
921         for(y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) {
922                 pass= passrect;
923                 shr= shrrect;
924                 od= offs;
925                 
926                 for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, shr+=osa, pass+=4, od++) {
927                         if(spart->mask[od] == 0) {
928                                 if(spart->addpassflag & SCE_PASS_VECTOR) 
929                                         add_transp_speed(rl, od, NULL, 0.0f, rdrect);
930                         }
931                         else {
932                                 if(re->osa == 0) {
933                                         addAlphaUnderFloat(pass, shr->combined);
934                                 }
935                                 else {
936                                         for(a=0; a<re->osa; a++)
937                                                 add_filt_fmask(1<<a, shr[a].combined, pass, rr->rectx);
938                                 }
939
940                                 if(spart->addpassflag) {
941                                         /* merge all in one, and then add */
942                                         merge_transp_passes(rl, shr);
943                                         add_transp_passes(rl, od, shr, pass[3]);
944
945                                         if(spart->addpassflag & SCE_PASS_VECTOR)
946                                                 add_transp_speed(rl, od, shr->winspeed, pass[3], rdrect);
947                                 }
948                         }
949                 }
950
951                 shrrect+= pa->rectx*osa;
952                 passrect+= 4*pa->rectx;
953                 offs+= pa->rectx;
954         }
955
956         /* disable scanline updating */
957         rr->renlay= NULL;
958 }
959
960 /* render call to fill in strands */
961 unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *rl, float *pass)
962 {
963         //struct RenderPrimitiveIterator *iter;
964         ObjectRen *obr;
965         ObjectInstanceRen *obi;
966         ZSpan zspan;
967         StrandRen *strand=0;
968         StrandVert *svert;
969         StrandPart spart;
970         StrandSegment sseg;
971         StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg;
972         MemArena *memarena;
973         float z[4], bounds[4], winmat[4][4];
974         int a, b, i, resultsize, totsegment, clip[4];
975
976         if(re->test_break())
977                 return NULL;
978         if(re->totstrand == 0)
979                 return NULL;
980
981         /* setup StrandPart */
982         memset(&spart, 0, sizeof(spart));
983
984         spart.re= re;
985         spart.rl= rl;
986         spart.pass= pass;
987         spart.rectx= pa->rectx;
988         spart.recty= pa->recty;
989         spart.rectz= pa->rectz;
990         spart.addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED);
991         spart.addzbuf= rl->passflag & SCE_PASS_Z;
992
993         if(re->osa) resultsize= pa->rectx*pa->recty*re->osa;
994         else resultsize= pa->rectx*pa->recty;
995         spart.result= MEM_callocN(sizeof(ShadeResult)*resultsize, "StrandPartResult");
996         spart.mask= MEM_callocN(pa->rectx*pa->recty*sizeof(short), "StrandPartMask");
997
998         if(spart.addpassflag & SCE_PASS_VECTOR) {
999                 /* initialize speed vectors */
1000                 for(a=0; a<resultsize; a++) {
1001                         spart.result[a].winspeed[0]= PASS_VECTOR_MAX;
1002                         spart.result[a].winspeed[1]= PASS_VECTOR_MAX;
1003                         spart.result[a].winspeed[2]= PASS_VECTOR_MAX;
1004                         spart.result[a].winspeed[3]= PASS_VECTOR_MAX;
1005                 }
1006         }
1007
1008         if(spart.addzbuf) {
1009                 /* duplicate rectz so we can read from the old buffer, while
1010                  * writing new z values */
1011                 spart.rectz= MEM_dupallocN(pa->rectz);
1012                 spart.outrectz= pa->rectz;
1013         }
1014
1015         shade_sample_initialize(&spart.ssamp1, pa, rl);
1016         shade_sample_initialize(&spart.ssamp2, pa, rl);
1017         shade_sample_initialize(&spart.ssamp, pa, rl);
1018         spart.ssamp1.shi[0].sample= 0;
1019         spart.ssamp2.shi[0].sample= 1;
1020         spart.ssamp1.tot= 1;
1021         spart.ssamp2.tot= 1;
1022         spart.ssamp.tot= 1;
1023
1024         zbuf_alloc_span(&zspan, pa->rectx, pa->recty, re->clipcrop);
1025
1026         /* needed for transform from hoco to zbuffer co */
1027         zspan.zmulx= ((float)re->winx)/2.0;
1028         zspan.zmuly= ((float)re->winy)/2.0;
1029         
1030         zspan.zofsx= -pa->disprect.xmin;
1031         zspan.zofsy= -pa->disprect.ymin;
1032
1033         /* to center the sample position */
1034         zspan.zofsx -= 0.5f;
1035         zspan.zofsy -= 0.5f;
1036
1037         /* clipping setup */
1038         bounds[0]= (2*pa->disprect.xmin - re->winx-1)/(float)re->winx;
1039         bounds[1]= (2*pa->disprect.xmax - re->winx+1)/(float)re->winx;
1040         bounds[2]= (2*pa->disprect.ymin - re->winy-1)/(float)re->winy;
1041         bounds[3]= (2*pa->disprect.ymax - re->winy+1)/(float)re->winy;
1042
1043         /* sort segments */
1044         //iter= init_primitive_iterator(re, re->strandbuckets, pa);
1045
1046         memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
1047         firstseg= NULL;
1048         sortseg= sortsegments;
1049         totsegment= 0;
1050
1051         //while((strand = next_primitive_iterator(iter))) {
1052         for(obi=re->instancetable.first, i=0; obi; obi=obi->next, i++) {
1053                 obr= obi->obr;
1054
1055                 if(obi->flag & R_TRANSFORMED)
1056                         zbuf_make_winmat(re, obi->mat, winmat);
1057                 else
1058                         zbuf_make_winmat(re, NULL, winmat);
1059
1060                 for(a=0; a<obr->totstrand; a++) {
1061                         if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
1062                         else strand++;
1063
1064                         if(re->test_break())
1065                                 break;
1066
1067                         if(!(strand->buffer->lay & rl->lay))
1068                                 continue;
1069
1070 #if 0
1071                         if(strand->clip)
1072                                 continue;
1073 #endif
1074
1075                         svert= strand->vert;
1076
1077                         /* keep clipping and z depth for 4 control points */
1078                         clip[1]= strand_test_clip(winmat, &zspan, bounds, svert->co, &z[1]);
1079                         clip[2]= strand_test_clip(winmat, &zspan, bounds, (svert+1)->co, &z[2]);
1080                         clip[0]= clip[1]; z[0]= z[1];
1081
1082                         for(b=0; b<strand->totvert-1; b++, svert++) {
1083                                 /* compute 4th point clipping and z depth */
1084                                 if(b < strand->totvert-2) {
1085                                         clip[3]= strand_test_clip(winmat, &zspan, bounds, (svert+2)->co, &z[3]);
1086                                 }
1087                                 else {
1088                                         clip[3]= clip[2]; z[3]= z[2];
1089                                 }
1090
1091                                 /* check clipping and add to sortsegments buffer */
1092                                 if(!(clip[0] & clip[1] & clip[2] & clip[3])) {
1093                                         sortseg= BLI_memarena_alloc(memarena, sizeof(StrandSortSegment));
1094                                         sortseg->obi= i;
1095                                         sortseg->strand= strand->index;
1096                                         sortseg->segment= b;
1097
1098                                         sortseg->z= 0.5f*(z[1] + z[2]);
1099
1100                                         sortseg->next= firstseg;
1101                                         firstseg= sortseg;
1102                                         totsegment++;
1103                                 }
1104
1105                                 /* shift clipping and z depth */
1106                                 clip[0]= clip[1]; z[0]= z[1];
1107                                 clip[1]= clip[2]; z[1]= z[2];
1108                                 clip[2]= clip[3]; z[2]= z[3];
1109                         }
1110                 }
1111         }
1112
1113 #if 0
1114         free_primitive_iterator(iter);
1115 #endif
1116
1117         if(!re->test_break()) {
1118                 /* convert list to array and sort */
1119                 sortsegments= MEM_mallocN(sizeof(StrandSortSegment)*totsegment, "StrandSortSegment");
1120                 for(a=0, sortseg=firstseg; a<totsegment; a++, sortseg=sortseg->next)
1121                         sortsegments[a]= *sortseg;
1122                 qsort(sortsegments, totsegment, sizeof(StrandSortSegment), compare_strand_segment);
1123         }
1124
1125         BLI_memarena_free(memarena);
1126
1127         spart.hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
1128
1129         if(!re->test_break()) {
1130                 /* render segments in sorted order */
1131                 sortseg= sortsegments;
1132                 for(a=0; a<totsegment; a++, sortseg++) {
1133                         if(re->test_break())
1134                                 break;
1135
1136                         obi= &re->objectinstance[sortseg->obi];
1137                         obr= obi->obr;
1138                         zbuf_make_winmat(re, NULL, winmat);
1139
1140                         sseg.obi= obi;
1141                         sseg.strand= RE_findOrAddStrand(obr, sortseg->strand);
1142                         sseg.buffer= sseg.strand->buffer;
1143                         sseg.sqadaptcos= sseg.buffer->adaptcos;
1144                         sseg.sqadaptcos *= sseg.sqadaptcos;
1145
1146                         svert= sseg.strand->vert + sortseg->segment;
1147                         sseg.v[0]= (sortseg->segment > 0)? (svert-1): svert;
1148                         sseg.v[1]= svert;
1149                         sseg.v[2]= svert+1;
1150                         sseg.v[3]= (sortseg->segment < sseg.strand->totvert-2)? svert+2: svert+1;
1151                         sseg.shaded= 0;
1152
1153                         spart.segment= &sseg;
1154
1155                         render_strand_segment(re, winmat, &spart, &zspan, 1, &sseg);
1156                 }
1157         }
1158
1159         // TODO printf(">>> %d\n", BLI_ghash_size(spart.hash));
1160         BLI_ghash_free(spart.hash, NULL, (GHashValFreeFP)MEM_freeN);
1161
1162         zbuffer_strands_filter(re, pa, rl, &spart, pass);
1163
1164         /* free */
1165         MEM_freeN(spart.result);
1166
1167         if(spart.addzbuf)
1168                 MEM_freeN(spart.rectz);
1169
1170         if(sortsegments)
1171                 MEM_freeN(sortsegments);
1172         
1173         zbuf_free_span(&zspan);
1174
1175         if(!(re->osa && (rl->layflag & SCE_LAY_SOLID))) {
1176                 MEM_freeN(spart.mask);
1177                 spart.mask= NULL;
1178         }
1179
1180         return spart.mask;
1181 }
1182
1183 void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *),  int do_pano, int do_buckets)
1184 {
1185 #if 0
1186         ObjectRen *obr;
1187         StrandRen *strand = NULL;
1188         StrandVert *svert;
1189         float hoco[4], min[2], max[2], bucketco[2], vec[3];
1190         int a, b;
1191         /* float bmin[3], bmax[3], bpad[3], padding[2]; */
1192         
1193         if(re->strandbuckets) {
1194                 free_buckets(re->strandbuckets);
1195                 re->strandbuckets= NULL;
1196         }
1197
1198         if(re->totstrand == 0)
1199                 return;
1200         
1201         if(do_buckets)
1202                 re->strandbuckets= init_buckets(re);
1203
1204         /* calculate view coordinates (and zbuffer value) */
1205         for(obr=re->objecttable.first; obr; obr=obr->next) {
1206                 for(a=0; a<obr->totstrand; a++) {
1207                         if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
1208                         else strand++;
1209
1210                         strand->clip= ~0;
1211
1212 #if 0
1213                         if(!(strand->buffer->flag & R_STRAND_BSPLINE)) {
1214                                 INIT_MINMAX(bmin, bmax);
1215                                 svert= strand->vert;
1216                                 for(b=0; b<strand->totvert; b++, svert++)
1217                                         DO_MINMAX(svert->co, bmin, bmax)
1218
1219                                 bpad[0]= (bmax[0]-bmin[0])*0.2f;
1220                                 bpad[1]= (bmax[1]-bmin[1])*0.2f;
1221                                 bpad[2]= (bmax[2]-bmin[2])*0.2f;
1222                         }
1223                         else
1224                                 bpad[0]= bpad[1]= bpad[2]= 0.0f;
1225
1226                         ma= strand->buffer->ma;
1227                         width= MAX2(ma->strand_sta, ma->strand_end);
1228                         if(strand->buffer->flag & R_STRAND_B_UNITS) {
1229                                 bpad[0] += 0.5f*width;
1230                                 bpad[1] += 0.5f*width;
1231                                 bpad[2] += 0.5f*width;
1232                         }
1233 #endif
1234
1235                         INIT_MINMAX2(min, max);
1236                         svert= strand->vert;
1237                         for(b=0; b<strand->totvert; b++, svert++) {
1238                                 //VECADD(vec, svert->co, bpad);
1239
1240                                 /* same as VertRen */
1241                                 if(do_pano) {
1242                                         vec[0]= re->panoco*svert->co[0] + re->panosi*svert->co[2];
1243                                         vec[1]= svert->co[1];
1244                                         vec[2]= -re->panosi*svert->co[0] + re->panoco*svert->co[2];
1245                                 }
1246                                 else
1247                                         VECCOPY(vec, svert->co)
1248
1249                                 /* Go from wcs to hcs ... */
1250                                 projectfunc(vec, re->winmat, hoco);
1251                                 /* ... and clip in that system. */
1252                                 strand->clip &= testclip(hoco);
1253
1254 #if 0
1255                                 if(do_buckets) {
1256                                         project_hoco_to_bucket(re->strandbuckets, hoco, bucketco);
1257                                         DO_MINMAX2(bucketco, min, max);
1258                                 }
1259 #endif
1260                         }
1261
1262 #if 0
1263                         if(do_buckets) {
1264                                 if(strand->buffer->flag & R_STRAND_BSPLINE) {
1265                                         min[0] -= width;
1266                                         min[1] -= width;
1267                                         max[0] += width;
1268                                         max[1] += width;
1269                                 }
1270                                 else {
1271                                         /* catmull-rom stays within 1.2f bounds in object space,
1272                                          * is this still true after projection? */
1273                                         min[0] -= width + (max[0]-min[0])*0.2f;
1274                                         min[1] -= width + (max[1]-min[1])*0.2f;
1275                                         max[0] += width + (max[0]-min[0])*0.2f;
1276                                         max[1] += width + (max[1]-min[1])*0.2f;
1277                                 }
1278
1279                                 add_buckets_primitive(re->strandbuckets, min, max, strand);
1280                         }
1281 #endif
1282                 }
1283         }
1284 #endif
1285 }
1286