Added new render pass: "Mist".
[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         vlr.obr= sseg->strand->buffer->obr;
691         if(sseg->buffer->ma->mode & MA_TANGENT_STR)
692                 vlr.flag |= R_TANGENT;
693
694         shi->vlr= &vlr;
695         shi->strand= sseg->strand;
696         shi->obi= sseg->obi;
697         shi->obr= sseg->obi->obr;
698
699         /* cache for shadow */
700         shi->samplenr++;
701
702         shade_input_set_strand(shi, sseg->strand, spoint);
703         shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint);
704         
705         /* init material vars */
706         // note, keep this synced with render_types.h
707         memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
708         shi->har= shi->mat->har;
709         
710         /* shade */
711         shade_samples_do_AO(ssamp);
712         shade_input_do_shade(shi, shr);
713
714         /* apply simplification */
715         strand_apply_shaderesult_alpha(shr, spoint->alpha);
716
717         /* include lamphalos for strand, since halo layer was added already */
718         if(re->flag & R_LAMPHALO)
719                 if(shi->layflag & SCE_LAY_HALO)
720                         renderspothalo(shi, shr->combined, shr->combined[3]);
721 }
722
723 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)
724 {
725         float jco1[3], jco2[3], jco3[3], jco4[3], jx, jy;
726
727         VECCOPY(jco1, co1);
728         VECCOPY(jco2, co2);
729         VECCOPY(jco3, co3);
730         VECCOPY(jco4, co4);
731
732         if(re->osa) {
733                 jx= -re->jit[sample][0];
734                 jy= -re->jit[sample][1];
735
736                 jco1[0] += jx; jco1[1] += jy;
737                 jco2[0] += jx; jco2[1] += jy;
738                 jco3[0] += jx; jco3[1] += jy;
739                 jco4[0] += jx; jco4[1] += jy;
740         }
741         else if(re->i.curblur) {
742                 jx= -re->jit[re->i.curblur-1][0];
743                 jy= -re->jit[re->i.curblur-1][1];
744
745                 jco1[0] += jx; jco1[1] += jy;
746                 jco2[0] += jx; jco2[1] += jy;
747                 jco3[0] += jx; jco3[1] += jy;
748                 jco4[0] += jx; jco4[1] += jy;
749         }
750
751         spart->sample= sample;
752
753         spart->t[0]= t-dt;
754         spart->s[0]= -1.0f;
755         spart->t[1]= t-dt;
756         spart->s[1]= 1.0f;
757         spart->t[2]= t;
758         spart->s[2]= 1.0f;
759         zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_blend);
760         spart->t[0]= t-dt;
761         spart->s[0]= -1.0f;
762         spart->t[1]= t;
763         spart->s[1]= 1.0f;
764         spart->t[2]= t;
765         spart->s[2]= -1.0f;
766         zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_blend);
767 }
768
769 static void strand_render(Render *re, StrandSegment *sseg, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandPoint *p1, StrandPoint *p2)
770 {
771         if(spart) {
772                 float t= p2->t;
773                 float dt= p2->t - p1->t;
774                 int a;
775
776                 if(re->osa) {
777                         for(a=0; a<re->osa; a++)
778                                 do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, a);
779                 }
780                 else
781                         do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, 0);
782         }
783         else {
784                 float hoco1[4], hoco2[3];
785                 int a, obi, index;
786   
787                 obi= sseg->obi - re->objectinstance;
788                 index= sseg->strand->index;
789
790                 projectvert(p1->co, winmat, hoco1);
791                 projectvert(p2->co, winmat, hoco2);
792   
793                 for(a=0; a<totzspan; a++) {
794 #if 0
795                         /* render both strand and single pixel wire to counter aliasing */
796                         zbufclip4(re, &zspan[a], obi, index, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, p1->clip2, p1->clip1, p2->clip1, p2->clip2);
797 #endif
798                         /* only render a line for now, which makes the shadow map more
799                            similiar across frames, and so reduces flicker */
800                         zbufsinglewire(&zspan[a], obi, index, hoco1, hoco2);
801                 }
802         }
803 }
804   
805 static int strand_segment_recursive(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth)
806 {
807         StrandPoint p;
808         StrandBuffer *buffer= sseg->buffer;
809         float dot, d1[2], d2[2], len1, len2;
810
811         if(depth == buffer->maxdepth)
812                 return 0;
813
814         p.t= (p1->t + p2->t)*0.5f;
815         strand_eval_point(sseg, &p);
816         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, &p);
817
818         d1[0]= (p.x - p1->x);
819         d1[1]= (p.y - p1->y);
820         len1= d1[0]*d1[0] + d1[1]*d1[1];
821
822         d2[0]= (p2->x - p.x);
823         d2[1]= (p2->y - p.y);
824         len2= d2[0]*d2[0] + d2[1]*d2[1];
825
826         if(len1 == 0.0f || len2 == 0.0f)
827                 return 0;
828         
829         dot= d1[0]*d2[0] + d1[1]*d2[1];
830         if(dot*dot > sseg->sqadaptcos*len1*len2)
831                 return 0;
832
833         if(spart) {
834                 do_strand_point_project(winmat, zspan, p.co1, p.hoco1, p.zco1);
835                 do_strand_point_project(winmat, zspan, p.co2, p.hoco2, p.zco2);
836         }
837         else {
838 #if 0
839                 projectvert(p.co1, winmat, p.hoco1);
840                 projectvert(p.co2, winmat, p.hoco2);
841                 p.clip1= testclip(p.hoco1);
842                 p.clip2= testclip(p.hoco2);
843 #endif
844         }
845
846         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, &p, depth+1))
847                 strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, &p);
848         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, &p, p2, depth+1))
849                 strand_render(re, sseg, winmat, spart, zspan, totzspan, &p, p2);
850         
851         return 1;
852 }
853
854 void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg)
855 {
856         StrandBuffer *buffer= sseg->buffer;
857         StrandPoint *p1= &sseg->point1;
858         StrandPoint *p2= &sseg->point2;
859
860         p1->t= 0.0f;
861         p2->t= 1.0f;
862
863         strand_eval_point(sseg, p1);
864         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p1);
865         strand_eval_point(sseg, p2);
866         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p2);
867
868         if(spart) {
869                 do_strand_point_project(winmat, zspan, p1->co1, p1->hoco1, p1->zco1);
870                 do_strand_point_project(winmat, zspan, p1->co2, p1->hoco2, p1->zco2);
871                 do_strand_point_project(winmat, zspan, p2->co1, p2->hoco1, p2->zco1);
872                 do_strand_point_project(winmat, zspan, p2->co2, p2->hoco2, p2->zco2);
873         }
874         else {
875 #if 0
876                 projectvert(p1->co1, winmat, p1->hoco1);
877                 projectvert(p1->co2, winmat, p1->hoco2);
878                 projectvert(p2->co1, winmat, p2->hoco1);
879                 projectvert(p2->co2, winmat, p2->hoco2);
880                 p1->clip1= testclip(p1->hoco1);
881                 p1->clip2= testclip(p1->hoco2);
882                 p2->clip1= testclip(p2->hoco1);
883                 p2->clip2= testclip(p2->hoco2);
884 #endif
885         }
886
887         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, p2, 0))
888                 strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, p2);
889 }
890
891 static void zbuffer_strands_filter(Render *re, RenderPart *pa, RenderLayer *rl, StrandPart *spart, float *pass)
892 {
893         RenderResult *rr= pa->result;
894         ShadeResult *shr, *shrrect= spart->result;
895         float *passrect= pass;
896         long *rdrect;
897         int osa, x, y, a, crop= 0, offs=0, od;
898
899         osa= (re->osa? re->osa: 1);
900
901         /* filtered render, for now we assume only 1 filter size */
902         if(pa->crop) {
903                 crop= 1;
904                 offs= pa->rectx + 1;
905                 passrect+= 4*offs;
906                 shrrect+= offs*osa;
907         }
908
909         rdrect= pa->rectdaps;
910
911         /* zero alpha pixels get speed vector max again */
912         if(spart->addpassflag & SCE_PASS_VECTOR)
913                 if(rl->layflag & SCE_LAY_SOLID)
914                         reset_sky_speedvectors(pa, rl, rl->scolrect);
915
916         /* init scanline updates */
917         rr->renrect.ymin= 0;
918         rr->renrect.ymax= -pa->crop;
919         rr->renlay= rl;
920         
921         /* filter the shade results */
922         for(y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) {
923                 pass= passrect;
924                 shr= shrrect;
925                 od= offs;
926                 
927                 for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, shr+=osa, pass+=4, od++) {
928                         if(spart->mask[od] == 0) {
929                                 if(spart->addpassflag & SCE_PASS_VECTOR) 
930                                         add_transp_speed(rl, od, NULL, 0.0f, rdrect);
931                         }
932                         else {
933                                 if(re->osa == 0) {
934                                         addAlphaUnderFloat(pass, shr->combined);
935                                 }
936                                 else {
937                                         for(a=0; a<re->osa; a++)
938                                                 add_filt_fmask(1<<a, shr[a].combined, pass, rr->rectx);
939                                 }
940
941                                 if(spart->addpassflag) {
942                                         /* merge all in one, and then add */
943                                         merge_transp_passes(rl, shr);
944                                         add_transp_passes(rl, od, shr, pass[3]);
945
946                                         if(spart->addpassflag & SCE_PASS_VECTOR)
947                                                 add_transp_speed(rl, od, shr->winspeed, pass[3], rdrect);
948                                 }
949                         }
950                 }
951
952                 shrrect+= pa->rectx*osa;
953                 passrect+= 4*pa->rectx;
954                 offs+= pa->rectx;
955         }
956
957         /* disable scanline updating */
958         rr->renlay= NULL;
959 }
960
961 /* render call to fill in strands */
962 unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *rl, float *pass)
963 {
964         //struct RenderPrimitiveIterator *iter;
965         ObjectRen *obr;
966         ObjectInstanceRen *obi;
967         ZSpan zspan;
968         StrandRen *strand=0;
969         StrandVert *svert;
970         StrandPart spart;
971         StrandSegment sseg;
972         StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg;
973         MemArena *memarena;
974         float z[4], bounds[4], winmat[4][4];
975         int a, b, i, resultsize, totsegment, clip[4];
976
977         if(re->test_break())
978                 return NULL;
979         if(re->totstrand == 0)
980                 return NULL;
981
982         /* setup StrandPart */
983         memset(&spart, 0, sizeof(spart));
984
985         spart.re= re;
986         spart.rl= rl;
987         spart.pass= pass;
988         spart.rectx= pa->rectx;
989         spart.recty= pa->recty;
990         spart.rectz= pa->rectz;
991         spart.addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED);
992         spart.addzbuf= rl->passflag & SCE_PASS_Z;
993
994         if(re->osa) resultsize= pa->rectx*pa->recty*re->osa;
995         else resultsize= pa->rectx*pa->recty;
996         spart.result= MEM_callocN(sizeof(ShadeResult)*resultsize, "StrandPartResult");
997         spart.mask= MEM_callocN(pa->rectx*pa->recty*sizeof(short), "StrandPartMask");
998
999         if(spart.addpassflag & SCE_PASS_VECTOR) {
1000                 /* initialize speed vectors */
1001                 for(a=0; a<resultsize; a++) {
1002                         spart.result[a].winspeed[0]= PASS_VECTOR_MAX;
1003                         spart.result[a].winspeed[1]= PASS_VECTOR_MAX;
1004                         spart.result[a].winspeed[2]= PASS_VECTOR_MAX;
1005                         spart.result[a].winspeed[3]= PASS_VECTOR_MAX;
1006                 }
1007         }
1008
1009         if(spart.addzbuf) {
1010                 /* duplicate rectz so we can read from the old buffer, while
1011                  * writing new z values */
1012                 spart.rectz= MEM_dupallocN(pa->rectz);
1013                 spart.outrectz= pa->rectz;
1014         }
1015
1016         shade_sample_initialize(&spart.ssamp1, pa, rl);
1017         shade_sample_initialize(&spart.ssamp2, pa, rl);
1018         shade_sample_initialize(&spart.ssamp, pa, rl);
1019         spart.ssamp1.shi[0].sample= 0;
1020         spart.ssamp2.shi[0].sample= 1;
1021         spart.ssamp1.tot= 1;
1022         spart.ssamp2.tot= 1;
1023         spart.ssamp.tot= 1;
1024
1025         zbuf_alloc_span(&zspan, pa->rectx, pa->recty, re->clipcrop);
1026
1027         /* needed for transform from hoco to zbuffer co */
1028         zspan.zmulx= ((float)re->winx)/2.0;
1029         zspan.zmuly= ((float)re->winy)/2.0;
1030         
1031         zspan.zofsx= -pa->disprect.xmin;
1032         zspan.zofsy= -pa->disprect.ymin;
1033
1034         /* to center the sample position */
1035         zspan.zofsx -= 0.5f;
1036         zspan.zofsy -= 0.5f;
1037
1038         /* clipping setup */
1039         bounds[0]= (2*pa->disprect.xmin - re->winx-1)/(float)re->winx;
1040         bounds[1]= (2*pa->disprect.xmax - re->winx+1)/(float)re->winx;
1041         bounds[2]= (2*pa->disprect.ymin - re->winy-1)/(float)re->winy;
1042         bounds[3]= (2*pa->disprect.ymax - re->winy+1)/(float)re->winy;
1043
1044         /* sort segments */
1045         //iter= init_primitive_iterator(re, re->strandbuckets, pa);
1046
1047         memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
1048         firstseg= NULL;
1049         sortseg= sortsegments;
1050         totsegment= 0;
1051
1052         //while((strand = next_primitive_iterator(iter))) {
1053         for(obi=re->instancetable.first, i=0; obi; obi=obi->next, i++) {
1054                 obr= obi->obr;
1055
1056                 if(obi->flag & R_TRANSFORMED)
1057                         zbuf_make_winmat(re, obi->mat, winmat);
1058                 else
1059                         zbuf_make_winmat(re, NULL, winmat);
1060
1061                 for(a=0; a<obr->totstrand; a++) {
1062                         if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
1063                         else strand++;
1064
1065                         if(re->test_break())
1066                                 break;
1067
1068                         if(!(strand->buffer->lay & rl->lay))
1069                                 continue;
1070
1071 #if 0
1072                         if(strand->clip)
1073                                 continue;
1074 #endif
1075
1076                         svert= strand->vert;
1077
1078                         /* keep clipping and z depth for 4 control points */
1079                         clip[1]= strand_test_clip(winmat, &zspan, bounds, svert->co, &z[1]);
1080                         clip[2]= strand_test_clip(winmat, &zspan, bounds, (svert+1)->co, &z[2]);
1081                         clip[0]= clip[1]; z[0]= z[1];
1082
1083                         for(b=0; b<strand->totvert-1; b++, svert++) {
1084                                 /* compute 4th point clipping and z depth */
1085                                 if(b < strand->totvert-2) {
1086                                         clip[3]= strand_test_clip(winmat, &zspan, bounds, (svert+2)->co, &z[3]);
1087                                 }
1088                                 else {
1089                                         clip[3]= clip[2]; z[3]= z[2];
1090                                 }
1091
1092                                 /* check clipping and add to sortsegments buffer */
1093                                 if(!(clip[0] & clip[1] & clip[2] & clip[3])) {
1094                                         sortseg= BLI_memarena_alloc(memarena, sizeof(StrandSortSegment));
1095                                         sortseg->obi= i;
1096                                         sortseg->strand= strand->index;
1097                                         sortseg->segment= b;
1098
1099                                         sortseg->z= 0.5f*(z[1] + z[2]);
1100
1101                                         sortseg->next= firstseg;
1102                                         firstseg= sortseg;
1103                                         totsegment++;
1104                                 }
1105
1106                                 /* shift clipping and z depth */
1107                                 clip[0]= clip[1]; z[0]= z[1];
1108                                 clip[1]= clip[2]; z[1]= z[2];
1109                                 clip[2]= clip[3]; z[2]= z[3];
1110                         }
1111                 }
1112         }
1113
1114 #if 0
1115         free_primitive_iterator(iter);
1116 #endif
1117
1118         if(!re->test_break()) {
1119                 /* convert list to array and sort */
1120                 sortsegments= MEM_mallocN(sizeof(StrandSortSegment)*totsegment, "StrandSortSegment");
1121                 for(a=0, sortseg=firstseg; a<totsegment; a++, sortseg=sortseg->next)
1122                         sortsegments[a]= *sortseg;
1123                 qsort(sortsegments, totsegment, sizeof(StrandSortSegment), compare_strand_segment);
1124         }
1125
1126         BLI_memarena_free(memarena);
1127
1128         spart.hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
1129
1130         if(!re->test_break()) {
1131                 /* render segments in sorted order */
1132                 sortseg= sortsegments;
1133                 for(a=0; a<totsegment; a++, sortseg++) {
1134                         if(re->test_break())
1135                                 break;
1136
1137                         obi= &re->objectinstance[sortseg->obi];
1138                         obr= obi->obr;
1139                         zbuf_make_winmat(re, NULL, winmat);
1140
1141                         sseg.obi= obi;
1142                         sseg.strand= RE_findOrAddStrand(obr, sortseg->strand);
1143                         sseg.buffer= sseg.strand->buffer;
1144                         sseg.sqadaptcos= sseg.buffer->adaptcos;
1145                         sseg.sqadaptcos *= sseg.sqadaptcos;
1146
1147                         svert= sseg.strand->vert + sortseg->segment;
1148                         sseg.v[0]= (sortseg->segment > 0)? (svert-1): svert;
1149                         sseg.v[1]= svert;
1150                         sseg.v[2]= svert+1;
1151                         sseg.v[3]= (sortseg->segment < sseg.strand->totvert-2)? svert+2: svert+1;
1152                         sseg.shaded= 0;
1153
1154                         spart.segment= &sseg;
1155
1156                         render_strand_segment(re, winmat, &spart, &zspan, 1, &sseg);
1157                 }
1158         }
1159
1160         // TODO printf(">>> %d\n", BLI_ghash_size(spart.hash));
1161         BLI_ghash_free(spart.hash, NULL, (GHashValFreeFP)MEM_freeN);
1162
1163         zbuffer_strands_filter(re, pa, rl, &spart, pass);
1164
1165         /* free */
1166         MEM_freeN(spart.result);
1167
1168         if(spart.addzbuf)
1169                 MEM_freeN(spart.rectz);
1170
1171         if(sortsegments)
1172                 MEM_freeN(sortsegments);
1173         
1174         zbuf_free_span(&zspan);
1175
1176         if(!(re->osa && (rl->layflag & SCE_LAY_SOLID))) {
1177                 MEM_freeN(spart.mask);
1178                 spart.mask= NULL;
1179         }
1180
1181         return spart.mask;
1182 }
1183
1184 void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *),  int do_pano, int do_buckets)
1185 {
1186 #if 0
1187         ObjectRen *obr;
1188         StrandRen *strand = NULL;
1189         StrandVert *svert;
1190         float hoco[4], min[2], max[2], bucketco[2], vec[3];
1191         int a, b;
1192         /* float bmin[3], bmax[3], bpad[3], padding[2]; */
1193         
1194         if(re->strandbuckets) {
1195                 free_buckets(re->strandbuckets);
1196                 re->strandbuckets= NULL;
1197         }
1198
1199         if(re->totstrand == 0)
1200                 return;
1201         
1202         if(do_buckets)
1203                 re->strandbuckets= init_buckets(re);
1204
1205         /* calculate view coordinates (and zbuffer value) */
1206         for(obr=re->objecttable.first; obr; obr=obr->next) {
1207                 for(a=0; a<obr->totstrand; a++) {
1208                         if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
1209                         else strand++;
1210
1211                         strand->clip= ~0;
1212
1213 #if 0
1214                         if(!(strand->buffer->flag & R_STRAND_BSPLINE)) {
1215                                 INIT_MINMAX(bmin, bmax);
1216                                 svert= strand->vert;
1217                                 for(b=0; b<strand->totvert; b++, svert++)
1218                                         DO_MINMAX(svert->co, bmin, bmax)
1219
1220                                 bpad[0]= (bmax[0]-bmin[0])*0.2f;
1221                                 bpad[1]= (bmax[1]-bmin[1])*0.2f;
1222                                 bpad[2]= (bmax[2]-bmin[2])*0.2f;
1223                         }
1224                         else
1225                                 bpad[0]= bpad[1]= bpad[2]= 0.0f;
1226
1227                         ma= strand->buffer->ma;
1228                         width= MAX2(ma->strand_sta, ma->strand_end);
1229                         if(strand->buffer->flag & R_STRAND_B_UNITS) {
1230                                 bpad[0] += 0.5f*width;
1231                                 bpad[1] += 0.5f*width;
1232                                 bpad[2] += 0.5f*width;
1233                         }
1234 #endif
1235
1236                         INIT_MINMAX2(min, max);
1237                         svert= strand->vert;
1238                         for(b=0; b<strand->totvert; b++, svert++) {
1239                                 //VECADD(vec, svert->co, bpad);
1240
1241                                 /* same as VertRen */
1242                                 if(do_pano) {
1243                                         vec[0]= re->panoco*svert->co[0] + re->panosi*svert->co[2];
1244                                         vec[1]= svert->co[1];
1245                                         vec[2]= -re->panosi*svert->co[0] + re->panoco*svert->co[2];
1246                                 }
1247                                 else
1248                                         VECCOPY(vec, svert->co)
1249
1250                                 /* Go from wcs to hcs ... */
1251                                 projectfunc(vec, re->winmat, hoco);
1252                                 /* ... and clip in that system. */
1253                                 strand->clip &= testclip(hoco);
1254
1255 #if 0
1256                                 if(do_buckets) {
1257                                         project_hoco_to_bucket(re->strandbuckets, hoco, bucketco);
1258                                         DO_MINMAX2(bucketco, min, max);
1259                                 }
1260 #endif
1261                         }
1262
1263 #if 0
1264                         if(do_buckets) {
1265                                 if(strand->buffer->flag & R_STRAND_BSPLINE) {
1266                                         min[0] -= width;
1267                                         min[1] -= width;
1268                                         max[0] += width;
1269                                         max[1] += width;
1270                                 }
1271                                 else {
1272                                         /* catmull-rom stays within 1.2f bounds in object space,
1273                                          * is this still true after projection? */
1274                                         min[0] -= width + (max[0]-min[0])*0.2f;
1275                                         min[1] -= width + (max[1]-min[1])*0.2f;
1276                                         max[0] += width + (max[0]-min[0])*0.2f;
1277                                         max[1] += width + (max[1]-min[1])*0.2f;
1278                                 }
1279
1280                                 add_buckets_primitive(re->strandbuckets, min, max, strand);
1281                         }
1282 #endif
1283                 }
1284         }
1285 #endif
1286 }
1287