svn merge -r 13357:13382 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[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         long *rectdaps;
432         unsigned short *mask;
433         int rectx, recty;
434         int addpassflag, addzbuf, sample;
435
436         StrandSegment *segment;
437         GHash *hash;
438         StrandPoint point1, point2;
439         ShadeSample ssamp1, ssamp2, ssamp;
440         float t[3], s[3];
441 } StrandPart;
442
443 typedef struct StrandSortSegment {
444         struct StrandSortSegment *next;
445         int obi, strand, segment;
446         float z;
447 } StrandSortSegment;
448
449 static int compare_strand_segment(const void *poin1, const void *poin2)
450 {
451         const StrandSortSegment *seg1= (const StrandSortSegment*)poin1;
452         const StrandSortSegment *seg2= (const StrandSortSegment*)poin2;
453
454         if(seg1->z < seg2->z)
455                 return -1;
456         else if(seg1->z == seg2->z)
457                 return 0;
458         else
459                 return 1;
460 }
461
462 static void interpolate_vec1(float *v1, float *v2, float t, float negt, float *v)
463 {
464         v[0]= negt*v1[0] + t*v2[0];
465 }
466
467 static void interpolate_vec3(float *v1, float *v2, float t, float negt, float *v)
468 {
469         v[0]= negt*v1[0] + t*v2[0];
470         v[1]= negt*v1[1] + t*v2[1];
471         v[2]= negt*v1[2] + t*v2[2];
472 }
473
474 static void interpolate_vec4(float *v1, float *v2, float t, float negt, float *v)
475 {
476         v[0]= negt*v1[0] + t*v2[0];
477         v[1]= negt*v1[1] + t*v2[1];
478         v[2]= negt*v1[2] + t*v2[2];
479         v[3]= negt*v1[3] + t*v2[3];
480 }
481
482 static void interpolate_shade_result(ShadeResult *shr1, ShadeResult *shr2, float t, ShadeResult *shr, int addpassflag)
483 {
484         float negt= 1.0f - t;
485
486         interpolate_vec4(shr1->combined, shr2->combined, t, negt, shr->combined);
487
488         if(addpassflag & SCE_PASS_VECTOR) {
489                 interpolate_vec4(shr1->winspeed, shr2->winspeed, t, negt, shr->winspeed);
490         }
491         /* optim... */
492         if(addpassflag & ~(SCE_PASS_VECTOR)) {
493                 if(addpassflag & SCE_PASS_RGBA)
494                         interpolate_vec4(shr1->col, shr2->col, t, negt, shr->col);
495                 if(addpassflag & SCE_PASS_NORMAL) {
496                         interpolate_vec3(shr1->nor, shr2->nor, t, negt, shr->nor);
497                         Normalize(shr->nor);
498                 }
499                 if(addpassflag & SCE_PASS_DIFFUSE)
500                         interpolate_vec3(shr1->diff, shr2->diff, t, negt, shr->diff);
501                 if(addpassflag & SCE_PASS_SPEC)
502                         interpolate_vec3(shr1->spec, shr2->spec, t, negt, shr->spec);
503                 if(addpassflag & SCE_PASS_SHADOW)
504                         interpolate_vec3(shr1->shad, shr2->shad, t, negt, shr->shad);
505                 if(addpassflag & SCE_PASS_AO)
506                         interpolate_vec3(shr1->ao, shr2->ao, t, negt, shr->ao);
507                 if(addpassflag & SCE_PASS_REFLECT)
508                         interpolate_vec3(shr1->refl, shr2->refl, t, negt, shr->refl);
509                 if(addpassflag & SCE_PASS_REFRACT)
510                         interpolate_vec3(shr1->refr, shr2->refr, t, negt, shr->refr);
511                 if(addpassflag & SCE_PASS_RADIO)
512                         interpolate_vec3(shr1->rad, shr2->rad, t, negt, shr->rad);
513                 if(addpassflag & SCE_PASS_MIST)
514                         interpolate_vec1(&shr1->mist, &shr2->mist, t, negt, &shr->mist);
515         }
516 }
517
518 static void add_strand_obindex(RenderLayer *rl, int offset, ObjectRen *obr)
519 {
520         RenderPass *rpass;
521         
522         for(rpass= rl->passes.first; rpass; rpass= rpass->next) {
523                 if(rpass->passtype == SCE_PASS_INDEXOB) {
524                         float *fp= rpass->rect + offset;
525                         *fp= (float)obr->ob->index;
526                         break;
527                 }
528         }
529 }
530
531 static void strand_apply_shaderesult_alpha(ShadeResult *shr, float alpha)
532 {
533         if(alpha < 1.0f) {
534                 shr->combined[0] *= alpha;
535                 shr->combined[1] *= alpha;
536                 shr->combined[2] *= alpha;
537                 shr->combined[3] *= alpha;
538
539                 shr->col[0] *= alpha;
540                 shr->col[1] *= alpha;
541                 shr->col[2] *= alpha;
542                 shr->col[3] *= alpha;
543
544                 shr->alpha *= alpha;
545         }
546 }
547
548 static void do_strand_point_project(float winmat[][4], ZSpan *zspan, float *co, float *hoco, float *zco)
549 {
550         projectvert(co, winmat, hoco);
551         hoco_to_zco(zspan, zco, hoco);
552 }
553
554 static void strand_project_point(float winmat[][4], float winx, float winy, StrandPoint *spoint)
555 {
556         float div;
557
558         projectvert(spoint->co, winmat, spoint->hoco);
559
560         div= 1.0f/spoint->hoco[3];
561         spoint->x= spoint->hoco[0]*div*winx*0.5f;
562         spoint->y= spoint->hoco[1]*div*winy*0.5f;
563 }
564
565 static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint);
566
567 static void strand_shade_get(StrandPart *spart, int lookup, ShadeSample *ssamp, StrandPoint *spoint, StrandVert *svert, StrandSegment *sseg)
568 {
569         ShadeResult *hashshr;
570
571         if(lookup) {
572                 hashshr= BLI_ghash_lookup(spart->hash, svert);
573
574                 if(!hashshr) {
575                         strand_shade_point(spart->re, ssamp, sseg, spoint);
576
577                         hashshr= MEM_callocN(sizeof(ShadeResult), "HashShadeResult");
578                         *hashshr= ssamp->shr[0];
579                         BLI_ghash_insert(spart->hash, svert, hashshr);
580                 }
581                 else {
582                         ssamp->shr[0]= *hashshr;
583                         BLI_ghash_remove(spart->hash, svert, NULL, (GHashValFreeFP)MEM_freeN);
584                 }
585         }
586         else
587                 strand_shade_point(spart->re, ssamp, sseg, spoint);
588 }
589
590 static void strand_shade_segment(StrandPart *spart)
591 {
592         StrandSegment *sseg= spart->segment;
593         int first, last;
594
595         if(!sseg->shaded) {
596                 first= (sseg->v[1] == &sseg->strand->vert[0]);
597                 last= (sseg->v[2] == &sseg->strand->vert[sseg->strand->totvert-1]);
598
599                 strand_shade_get(spart, !first, &spart->ssamp1, &sseg->point1, sseg->v[1], sseg);
600                 strand_shade_get(spart, !last, &spart->ssamp2, &sseg->point2, sseg->v[2], sseg);
601                 sseg->shaded= 1;
602         }
603 }
604
605 static void do_strand_blend(void *handle, int x, int y, float u, float v, float z)
606 {
607         StrandPart *spart= (StrandPart*)handle;
608         StrandBuffer *buffer= spart->segment->buffer;
609         ShadeResult *shr;
610         float /**pass,*/ t, s;
611         int offset, zverg, bufferz;
612
613         /* check again solid z-buffer */
614         offset = y*spart->rectx + x;
615         zverg= (int)z;
616
617         if(spart->rectdaps) {
618                 /* find the z of the sample */
619                 PixStr *ps;
620                 long *rd= spart->rectdaps + offset;
621                 int sample= (1<<spart->sample);
622                 
623                 bufferz= 0x7FFFFFFF;
624                 
625                 if(*rd) {       
626                         for(ps= (PixStr *)(*rd); ps; ps= ps->next) {
627                                 if(sample & ps->mask) {
628                                         bufferz= ps->z;
629                                         break;
630                                 }
631                         }
632                 }
633         }
634         else
635                 bufferz= spart->rectz[offset];
636
637         if(zverg < bufferz) {
638                 /* fill in output z-buffer if needed */
639                 if(spart->addzbuf)
640                         if(zverg < spart->outrectz[offset])
641                                 spart->outrectz[offset]= zverg;
642
643                 /* check alpha limit */
644                 shr= spart->result + offset*(spart->re->osa? spart->re->osa: 1);
645                 if(shr[spart->sample].combined[3]>0.999f)
646                         return;
647
648                 /* shade points if not shaded yet */
649                 strand_shade_segment(spart);
650
651                 /* interpolate shading from two control points */
652                 t = u*spart->t[0] + v*spart->t[1] + (1.0f-u-v)*spart->t[2];
653                 interpolate_shade_result(spart->ssamp1.shr, spart->ssamp2.shr, t,
654                         spart->ssamp.shr, spart->addpassflag);
655
656                 /* alpha along width */
657                 if(buffer->widthfade != 0.0f) {
658                         s = fabs(u*spart->s[0] + v*spart->s[1] + (1.0f-u-v)*spart->s[2]);
659                         s = 1.0f - pow(s, buffer->widthfade);
660
661                         strand_apply_shaderesult_alpha(spart->ssamp.shr, s);
662                 }
663
664                 /* add in shaderesult array for part */
665                 spart->ssamp.shi[0].mask= (1<<spart->sample);
666                 addtosamp_shr(shr, &spart->ssamp, spart->addpassflag);
667                 spart->mask[offset] |= (1<<spart->sample);
668
669 #if 0
670                 /* fill in pass for preview */
671                 if(spart->sample == 0) {
672                         pass= spart->pass + offset*4;
673                         QUATCOPY(pass, shr->combined);
674                 }
675 #endif
676
677                 if(spart->addpassflag & SCE_PASS_INDEXOB)
678                         add_strand_obindex(spart->rl, offset, buffer->obr);
679         }
680 }
681
682 static int strand_test_clip(float winmat[][4], ZSpan *zspan, float *bounds, float *co, float *zcomp)
683 {
684         float hoco[4];
685         int clipflag= 0;
686
687         projectvert(co, winmat, hoco);
688
689         /* we compare z without perspective division for segment sorting */
690         *zcomp= hoco[2];
691
692         if(hoco[0] > bounds[1]*hoco[3]) clipflag |= 1;
693         else if(hoco[0]< bounds[0]*hoco[3]) clipflag |= 2;
694         else if(hoco[1] > bounds[3]*hoco[3]) clipflag |= 4;
695         else if(hoco[1]< bounds[2]*hoco[3]) clipflag |= 8;
696
697         clipflag |= testclip(hoco);
698
699         return clipflag;
700 }
701
702 static void strand_shade_point(Render *re, ShadeSample *ssamp, StrandSegment *sseg, StrandPoint *spoint)
703 {
704         ShadeInput *shi= ssamp->shi;
705         ShadeResult *shr= ssamp->shr;
706         VlakRen vlr;
707
708         memset(&vlr, 0, sizeof(vlr));
709         vlr.flag= R_SMOOTH;
710         if(sseg->buffer->ma->mode & MA_TANGENT_STR)
711                 vlr.flag |= R_TANGENT;
712
713         shi->vlr= &vlr;
714         shi->strand= sseg->strand;
715         shi->obi= sseg->obi;
716         shi->obr= sseg->obi->obr;
717
718         /* cache for shadow */
719         shi->samplenr++;
720
721         shade_input_set_strand(shi, sseg->strand, spoint);
722         shade_input_set_strand_texco(shi, sseg->strand, sseg->v[1], spoint);
723         
724         /* init material vars */
725         // note, keep this synced with render_types.h
726         memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
727         shi->har= shi->mat->har;
728         
729         /* shade */
730         shade_samples_do_AO(ssamp);
731         shade_input_do_shade(shi, shr);
732
733         /* apply simplification */
734         strand_apply_shaderesult_alpha(shr, spoint->alpha);
735
736         /* include lamphalos for strand, since halo layer was added already */
737         if(re->flag & R_LAMPHALO)
738                 if(shi->layflag & SCE_LAY_HALO)
739                         renderspothalo(shi, shr->combined, shr->combined[3]);
740 }
741
742 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)
743 {
744         float jco1[3], jco2[3], jco3[3], jco4[3], jx, jy;
745
746         VECCOPY(jco1, co1);
747         VECCOPY(jco2, co2);
748         VECCOPY(jco3, co3);
749         VECCOPY(jco4, co4);
750
751         if(re->osa) {
752                 jx= -re->jit[sample][0];
753                 jy= -re->jit[sample][1];
754
755                 jco1[0] += jx; jco1[1] += jy;
756                 jco2[0] += jx; jco2[1] += jy;
757                 jco3[0] += jx; jco3[1] += jy;
758                 jco4[0] += jx; jco4[1] += jy;
759         }
760         else if(re->i.curblur) {
761                 jx= -re->jit[re->i.curblur-1][0];
762                 jy= -re->jit[re->i.curblur-1][1];
763
764                 jco1[0] += jx; jco1[1] += jy;
765                 jco2[0] += jx; jco2[1] += jy;
766                 jco3[0] += jx; jco3[1] += jy;
767                 jco4[0] += jx; jco4[1] += jy;
768         }
769
770         spart->sample= sample;
771
772         spart->t[0]= t-dt;
773         spart->s[0]= -1.0f;
774         spart->t[1]= t-dt;
775         spart->s[1]= 1.0f;
776         spart->t[2]= t;
777         spart->s[2]= 1.0f;
778         zspan_scanconvert_strand(zspan, spart, jco1, jco2, jco3, do_strand_blend);
779         spart->t[0]= t-dt;
780         spart->s[0]= -1.0f;
781         spart->t[1]= t;
782         spart->s[1]= 1.0f;
783         spart->t[2]= t;
784         spart->s[2]= -1.0f;
785         zspan_scanconvert_strand(zspan, spart, jco1, jco3, jco4, do_strand_blend);
786 }
787
788 static void strand_render(Render *re, StrandSegment *sseg, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandPoint *p1, StrandPoint *p2)
789 {
790         if(spart) {
791                 float t= p2->t;
792                 float dt= p2->t - p1->t;
793                 int a;
794
795                 if(re->osa) {
796                         for(a=0; a<re->osa; a++)
797                                 do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, a);
798                 }
799                 else
800                         do_scanconvert_strand(re, spart, zspan, t, dt, p1->zco2, p1->zco1, p2->zco1, p2->zco2, 0);
801         }
802         else {
803                 float hoco1[4], hoco2[3];
804                 int a, obi, index;
805   
806                 obi= sseg->obi - re->objectinstance;
807                 index= sseg->strand->index;
808
809                 projectvert(p1->co, winmat, hoco1);
810                 projectvert(p2->co, winmat, hoco2);
811   
812                 for(a=0; a<totzspan; a++) {
813 #if 0
814                         /* render both strand and single pixel wire to counter aliasing */
815                         zbufclip4(re, &zspan[a], obi, index, p1->hoco2, p1->hoco1, p2->hoco1, p2->hoco2, p1->clip2, p1->clip1, p2->clip1, p2->clip2);
816 #endif
817                         /* only render a line for now, which makes the shadow map more
818                            similiar across frames, and so reduces flicker */
819                         zbufsinglewire(&zspan[a], obi, index, hoco1, hoco2);
820                 }
821         }
822 }
823   
824 static int strand_segment_recursive(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg, StrandPoint *p1, StrandPoint *p2, int depth)
825 {
826         StrandPoint p;
827         StrandBuffer *buffer= sseg->buffer;
828         float dot, d1[2], d2[2], len1, len2;
829
830         if(depth == buffer->maxdepth)
831                 return 0;
832
833         p.t= (p1->t + p2->t)*0.5f;
834         strand_eval_point(sseg, &p);
835         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, &p);
836
837         d1[0]= (p.x - p1->x);
838         d1[1]= (p.y - p1->y);
839         len1= d1[0]*d1[0] + d1[1]*d1[1];
840
841         d2[0]= (p2->x - p.x);
842         d2[1]= (p2->y - p.y);
843         len2= d2[0]*d2[0] + d2[1]*d2[1];
844
845         if(len1 == 0.0f || len2 == 0.0f)
846                 return 0;
847         
848         dot= d1[0]*d2[0] + d1[1]*d2[1];
849         if(dot*dot > sseg->sqadaptcos*len1*len2)
850                 return 0;
851
852         if(spart) {
853                 do_strand_point_project(winmat, zspan, p.co1, p.hoco1, p.zco1);
854                 do_strand_point_project(winmat, zspan, p.co2, p.hoco2, p.zco2);
855         }
856         else {
857 #if 0
858                 projectvert(p.co1, winmat, p.hoco1);
859                 projectvert(p.co2, winmat, p.hoco2);
860                 p.clip1= testclip(p.hoco1);
861                 p.clip2= testclip(p.hoco2);
862 #endif
863         }
864
865         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, &p, depth+1))
866                 strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, &p);
867         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, &p, p2, depth+1))
868                 strand_render(re, sseg, winmat, spart, zspan, totzspan, &p, p2);
869         
870         return 1;
871 }
872
873 void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSpan *zspan, int totzspan, StrandSegment *sseg)
874 {
875         StrandBuffer *buffer= sseg->buffer;
876         StrandPoint *p1= &sseg->point1;
877         StrandPoint *p2= &sseg->point2;
878
879         p1->t= 0.0f;
880         p2->t= 1.0f;
881
882         strand_eval_point(sseg, p1);
883         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p1);
884         strand_eval_point(sseg, p2);
885         strand_project_point(buffer->winmat, buffer->winx, buffer->winy, p2);
886
887         if(spart) {
888                 do_strand_point_project(winmat, zspan, p1->co1, p1->hoco1, p1->zco1);
889                 do_strand_point_project(winmat, zspan, p1->co2, p1->hoco2, p1->zco2);
890                 do_strand_point_project(winmat, zspan, p2->co1, p2->hoco1, p2->zco1);
891                 do_strand_point_project(winmat, zspan, p2->co2, p2->hoco2, p2->zco2);
892         }
893         else {
894 #if 0
895                 projectvert(p1->co1, winmat, p1->hoco1);
896                 projectvert(p1->co2, winmat, p1->hoco2);
897                 projectvert(p2->co1, winmat, p2->hoco1);
898                 projectvert(p2->co2, winmat, p2->hoco2);
899                 p1->clip1= testclip(p1->hoco1);
900                 p1->clip2= testclip(p1->hoco2);
901                 p2->clip1= testclip(p2->hoco1);
902                 p2->clip2= testclip(p2->hoco2);
903 #endif
904         }
905
906         if(!strand_segment_recursive(re, winmat, spart, zspan, totzspan, sseg, p1, p2, 0))
907                 strand_render(re, sseg, winmat, spart, zspan, totzspan, p1, p2);
908 }
909
910 static void zbuffer_strands_filter(Render *re, RenderPart *pa, RenderLayer *rl, StrandPart *spart, float *pass)
911 {
912         RenderResult *rr= pa->result;
913         ShadeResult *shr, *shrrect= spart->result;
914         float *passrect= pass, alpha, sampalpha;
915         long *rdrect;
916         int osa, x, y, a, crop= 0, offs=0, od;
917
918         osa= (re->osa? re->osa: 1);
919         sampalpha= 1.0f/osa;
920
921         /* filtered render, for now we assume only 1 filter size */
922         if(pa->crop) {
923                 crop= 1;
924                 offs= pa->rectx + 1;
925                 passrect+= 4*offs;
926                 shrrect+= offs*osa;
927         }
928
929         rdrect= pa->rectdaps;
930
931         /* zero alpha pixels get speed vector max again */
932         if(spart->addpassflag & SCE_PASS_VECTOR)
933                 if(rl->layflag & SCE_LAY_SOLID)
934                         reset_sky_speedvectors(pa, rl, rl->scolrect);
935
936         /* init scanline updates */
937         rr->renrect.ymin= 0;
938         rr->renrect.ymax= -pa->crop;
939         rr->renlay= rl;
940         
941         /* filter the shade results */
942         for(y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) {
943                 pass= passrect;
944                 shr= shrrect;
945                 od= offs;
946                 
947                 for(x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, shr+=osa, pass+=4, od++) {
948                         if(spart->mask[od] == 0) {
949                                 if(spart->addpassflag & SCE_PASS_VECTOR) 
950                                         add_transp_speed(rl, od, NULL, 0.0f, rdrect);
951                         }
952                         else {
953                                 alpha= 0.0f;
954
955                                 if(re->osa == 0) {
956                                         addAlphaUnderFloat(pass, shr->combined);
957                                 }
958                                 else {
959                                         /* note; cannot use pass[3] for alpha due to filtermask */
960                                         for(a=0; a<re->osa; a++) {
961                                                 add_filt_fmask(1<<a, shr[a].combined, pass, rr->rectx);
962                                                 alpha += shr[a].combined[3];
963                                         }
964                                 }
965
966                                 if(spart->addpassflag) {
967                                         alpha *= sampalpha;
968
969                                         /* merge all in one, and then add */
970                                         merge_transp_passes(rl, shr);
971                                         add_transp_passes(rl, od, shr, alpha);
972
973                                         if(spart->addpassflag & SCE_PASS_VECTOR)
974                                                 add_transp_speed(rl, od, shr->winspeed, alpha, rdrect);
975                                 }
976                         }
977                 }
978
979                 shrrect+= pa->rectx*osa;
980                 passrect+= 4*pa->rectx;
981                 offs+= pa->rectx;
982         }
983
984         /* disable scanline updating */
985         rr->renlay= NULL;
986 }
987
988 /* render call to fill in strands */
989 unsigned short *zbuffer_strands_shade(Render *re, RenderPart *pa, RenderLayer *rl, float *pass)
990 {
991         //struct RenderPrimitiveIterator *iter;
992         ObjectRen *obr;
993         ObjectInstanceRen *obi;
994         ZSpan zspan;
995         StrandRen *strand=0;
996         StrandVert *svert;
997         StrandPart spart;
998         StrandSegment sseg;
999         StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg;
1000         MemArena *memarena;
1001         float z[4], bounds[4], winmat[4][4];
1002         int a, b, i, resultsize, totsegment, clip[4];
1003
1004         if(re->test_break())
1005                 return NULL;
1006         if(re->totstrand == 0)
1007                 return NULL;
1008
1009         /* setup StrandPart */
1010         memset(&spart, 0, sizeof(spart));
1011
1012         spart.re= re;
1013         spart.rl= rl;
1014         spart.pass= pass;
1015         spart.rectx= pa->rectx;
1016         spart.recty= pa->recty;
1017         spart.rectz= pa->rectz;
1018         spart.rectdaps= pa->rectdaps;
1019         spart.addpassflag= rl->passflag & ~(SCE_PASS_Z|SCE_PASS_COMBINED);
1020         spart.addzbuf= rl->passflag & SCE_PASS_Z;
1021
1022         if(re->osa) resultsize= pa->rectx*pa->recty*re->osa;
1023         else resultsize= pa->rectx*pa->recty;
1024         spart.result= MEM_callocN(sizeof(ShadeResult)*resultsize, "StrandPartResult");
1025         spart.mask= MEM_callocN(pa->rectx*pa->recty*sizeof(short), "StrandPartMask");
1026
1027         if(spart.addpassflag & SCE_PASS_VECTOR) {
1028                 /* initialize speed vectors */
1029                 for(a=0; a<resultsize; a++) {
1030                         spart.result[a].winspeed[0]= PASS_VECTOR_MAX;
1031                         spart.result[a].winspeed[1]= PASS_VECTOR_MAX;
1032                         spart.result[a].winspeed[2]= PASS_VECTOR_MAX;
1033                         spart.result[a].winspeed[3]= PASS_VECTOR_MAX;
1034                 }
1035         }
1036
1037         if(spart.addzbuf) {
1038                 /* duplicate rectz so we can read from the old buffer, while
1039                  * writing new z values */
1040                 spart.rectz= MEM_dupallocN(pa->rectz);
1041                 spart.outrectz= pa->rectz;
1042         }
1043
1044         shade_sample_initialize(&spart.ssamp1, pa, rl);
1045         shade_sample_initialize(&spart.ssamp2, pa, rl);
1046         shade_sample_initialize(&spart.ssamp, pa, rl);
1047         spart.ssamp1.shi[0].sample= 0;
1048         spart.ssamp2.shi[0].sample= 1;
1049         spart.ssamp1.tot= 1;
1050         spart.ssamp2.tot= 1;
1051         spart.ssamp.tot= 1;
1052
1053         zbuf_alloc_span(&zspan, pa->rectx, pa->recty, re->clipcrop);
1054
1055         /* needed for transform from hoco to zbuffer co */
1056         zspan.zmulx= ((float)re->winx)/2.0;
1057         zspan.zmuly= ((float)re->winy)/2.0;
1058         
1059         zspan.zofsx= -pa->disprect.xmin;
1060         zspan.zofsy= -pa->disprect.ymin;
1061
1062         /* to center the sample position */
1063         zspan.zofsx -= 0.5f;
1064         zspan.zofsy -= 0.5f;
1065
1066         /* clipping setup */
1067         bounds[0]= (2*pa->disprect.xmin - re->winx-1)/(float)re->winx;
1068         bounds[1]= (2*pa->disprect.xmax - re->winx+1)/(float)re->winx;
1069         bounds[2]= (2*pa->disprect.ymin - re->winy-1)/(float)re->winy;
1070         bounds[3]= (2*pa->disprect.ymax - re->winy+1)/(float)re->winy;
1071
1072         /* sort segments */
1073         //iter= init_primitive_iterator(re, re->strandbuckets, pa);
1074
1075         memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
1076         firstseg= NULL;
1077         sortseg= sortsegments;
1078         totsegment= 0;
1079
1080         //while((strand = next_primitive_iterator(iter))) {
1081         for(obi=re->instancetable.first, i=0; obi; obi=obi->next, i++) {
1082                 obr= obi->obr;
1083
1084                 if(obi->flag & R_TRANSFORMED)
1085                         zbuf_make_winmat(re, obi->mat, winmat);
1086                 else
1087                         zbuf_make_winmat(re, NULL, winmat);
1088
1089                 for(a=0; a<obr->totstrand; a++) {
1090                         if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
1091                         else strand++;
1092
1093                         if(re->test_break())
1094                                 break;
1095
1096                         if(!(strand->buffer->lay & rl->lay))
1097                                 continue;
1098
1099 #if 0
1100                         if(strand->clip)
1101                                 continue;
1102 #endif
1103
1104                         svert= strand->vert;
1105
1106                         /* keep clipping and z depth for 4 control points */
1107                         clip[1]= strand_test_clip(winmat, &zspan, bounds, svert->co, &z[1]);
1108                         clip[2]= strand_test_clip(winmat, &zspan, bounds, (svert+1)->co, &z[2]);
1109                         clip[0]= clip[1]; z[0]= z[1];
1110
1111                         for(b=0; b<strand->totvert-1; b++, svert++) {
1112                                 /* compute 4th point clipping and z depth */
1113                                 if(b < strand->totvert-2) {
1114                                         clip[3]= strand_test_clip(winmat, &zspan, bounds, (svert+2)->co, &z[3]);
1115                                 }
1116                                 else {
1117                                         clip[3]= clip[2]; z[3]= z[2];
1118                                 }
1119
1120                                 /* check clipping and add to sortsegments buffer */
1121                                 if(!(clip[0] & clip[1] & clip[2] & clip[3])) {
1122                                         sortseg= BLI_memarena_alloc(memarena, sizeof(StrandSortSegment));
1123                                         sortseg->obi= i;
1124                                         sortseg->strand= strand->index;
1125                                         sortseg->segment= b;
1126
1127                                         sortseg->z= 0.5f*(z[1] + z[2]);
1128
1129                                         sortseg->next= firstseg;
1130                                         firstseg= sortseg;
1131                                         totsegment++;
1132                                 }
1133
1134                                 /* shift clipping and z depth */
1135                                 clip[0]= clip[1]; z[0]= z[1];
1136                                 clip[1]= clip[2]; z[1]= z[2];
1137                                 clip[2]= clip[3]; z[2]= z[3];
1138                         }
1139                 }
1140         }
1141
1142 #if 0
1143         free_primitive_iterator(iter);
1144 #endif
1145
1146         if(!re->test_break()) {
1147                 /* convert list to array and sort */
1148                 sortsegments= MEM_mallocN(sizeof(StrandSortSegment)*totsegment, "StrandSortSegment");
1149                 for(a=0, sortseg=firstseg; a<totsegment; a++, sortseg=sortseg->next)
1150                         sortsegments[a]= *sortseg;
1151                 qsort(sortsegments, totsegment, sizeof(StrandSortSegment), compare_strand_segment);
1152         }
1153
1154         BLI_memarena_free(memarena);
1155
1156         spart.hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
1157
1158         if(!re->test_break()) {
1159                 /* render segments in sorted order */
1160                 sortseg= sortsegments;
1161                 for(a=0; a<totsegment; a++, sortseg++) {
1162                         if(re->test_break())
1163                                 break;
1164
1165                         obi= &re->objectinstance[sortseg->obi];
1166                         obr= obi->obr;
1167                         zbuf_make_winmat(re, NULL, winmat);
1168
1169                         sseg.obi= obi;
1170                         sseg.strand= RE_findOrAddStrand(obr, sortseg->strand);
1171                         sseg.buffer= sseg.strand->buffer;
1172                         sseg.sqadaptcos= sseg.buffer->adaptcos;
1173                         sseg.sqadaptcos *= sseg.sqadaptcos;
1174
1175                         svert= sseg.strand->vert + sortseg->segment;
1176                         sseg.v[0]= (sortseg->segment > 0)? (svert-1): svert;
1177                         sseg.v[1]= svert;
1178                         sseg.v[2]= svert+1;
1179                         sseg.v[3]= (sortseg->segment < sseg.strand->totvert-2)? svert+2: svert+1;
1180                         sseg.shaded= 0;
1181
1182                         spart.segment= &sseg;
1183
1184                         render_strand_segment(re, winmat, &spart, &zspan, 1, &sseg);
1185                 }
1186         }
1187
1188         // TODO printf(">>> %d\n", BLI_ghash_size(spart.hash));
1189         BLI_ghash_free(spart.hash, NULL, (GHashValFreeFP)MEM_freeN);
1190
1191         zbuffer_strands_filter(re, pa, rl, &spart, pass);
1192
1193         /* free */
1194         MEM_freeN(spart.result);
1195
1196         if(spart.addzbuf)
1197                 MEM_freeN(spart.rectz);
1198
1199         if(sortsegments)
1200                 MEM_freeN(sortsegments);
1201         
1202         zbuf_free_span(&zspan);
1203
1204         if(!(re->osa && (rl->layflag & SCE_LAY_SOLID))) {
1205                 MEM_freeN(spart.mask);
1206                 spart.mask= NULL;
1207         }
1208
1209         return spart.mask;
1210 }
1211
1212 void project_strands(Render *re, void (*projectfunc)(float *, float mat[][4], float *),  int do_pano, int do_buckets)
1213 {
1214 #if 0
1215         ObjectRen *obr;
1216         StrandRen *strand = NULL;
1217         StrandVert *svert;
1218         float hoco[4], min[2], max[2], bucketco[2], vec[3];
1219         int a, b;
1220         /* float bmin[3], bmax[3], bpad[3], padding[2]; */
1221         
1222         if(re->strandbuckets) {
1223                 free_buckets(re->strandbuckets);
1224                 re->strandbuckets= NULL;
1225         }
1226
1227         if(re->totstrand == 0)
1228                 return;
1229         
1230         if(do_buckets)
1231                 re->strandbuckets= init_buckets(re);
1232
1233         /* calculate view coordinates (and zbuffer value) */
1234         for(obr=re->objecttable.first; obr; obr=obr->next) {
1235                 for(a=0; a<obr->totstrand; a++) {
1236                         if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
1237                         else strand++;
1238
1239                         strand->clip= ~0;
1240
1241 #if 0
1242                         if(!(strand->buffer->flag & R_STRAND_BSPLINE)) {
1243                                 INIT_MINMAX(bmin, bmax);
1244                                 svert= strand->vert;
1245                                 for(b=0; b<strand->totvert; b++, svert++)
1246                                         DO_MINMAX(svert->co, bmin, bmax)
1247
1248                                 bpad[0]= (bmax[0]-bmin[0])*0.2f;
1249                                 bpad[1]= (bmax[1]-bmin[1])*0.2f;
1250                                 bpad[2]= (bmax[2]-bmin[2])*0.2f;
1251                         }
1252                         else
1253                                 bpad[0]= bpad[1]= bpad[2]= 0.0f;
1254
1255                         ma= strand->buffer->ma;
1256                         width= MAX2(ma->strand_sta, ma->strand_end);
1257                         if(strand->buffer->flag & R_STRAND_B_UNITS) {
1258                                 bpad[0] += 0.5f*width;
1259                                 bpad[1] += 0.5f*width;
1260                                 bpad[2] += 0.5f*width;
1261                         }
1262 #endif
1263
1264                         INIT_MINMAX2(min, max);
1265                         svert= strand->vert;
1266                         for(b=0; b<strand->totvert; b++, svert++) {
1267                                 //VECADD(vec, svert->co, bpad);
1268
1269                                 /* same as VertRen */
1270                                 if(do_pano) {
1271                                         vec[0]= re->panoco*svert->co[0] + re->panosi*svert->co[2];
1272                                         vec[1]= svert->co[1];
1273                                         vec[2]= -re->panosi*svert->co[0] + re->panoco*svert->co[2];
1274                                 }
1275                                 else
1276                                         VECCOPY(vec, svert->co)
1277
1278                                 /* Go from wcs to hcs ... */
1279                                 projectfunc(vec, re->winmat, hoco);
1280                                 /* ... and clip in that system. */
1281                                 strand->clip &= testclip(hoco);
1282
1283 #if 0
1284                                 if(do_buckets) {
1285                                         project_hoco_to_bucket(re->strandbuckets, hoco, bucketco);
1286                                         DO_MINMAX2(bucketco, min, max);
1287                                 }
1288 #endif
1289                         }
1290
1291 #if 0
1292                         if(do_buckets) {
1293                                 if(strand->buffer->flag & R_STRAND_BSPLINE) {
1294                                         min[0] -= width;
1295                                         min[1] -= width;
1296                                         max[0] += width;
1297                                         max[1] += width;
1298                                 }
1299                                 else {
1300                                         /* catmull-rom stays within 1.2f bounds in object space,
1301                                          * is this still true after projection? */
1302                                         min[0] -= width + (max[0]-min[0])*0.2f;
1303                                         min[1] -= width + (max[1]-min[1])*0.2f;
1304                                         max[0] += width + (max[0]-min[0])*0.2f;
1305                                         max[1] += width + (max[1]-min[1])*0.2f;
1306                                 }
1307
1308                                 add_buckets_primitive(re->strandbuckets, min, max, strand);
1309                         }
1310 #endif
1311                 }
1312         }
1313 #endif
1314 }
1315