Three features;
[blender-staging.git] / source / blender / render / intern / source / renderdatabase.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  * Contributor(s): 2004-2006, Blender Foundation, full recode
24  *
25  * ***** END GPL/BL DUAL LICENSE BLOCK *****
26  */
27
28 /*
29  * Storage, retrieval and query of render specific data.
30  *
31  * All data from a Blender scene is converter by the renderconverter/
32  * into a special format that is used by the render module to make
33  * images out of. These functions interface to the render-specific
34  * database.  
35  *
36  * The blo{ha/ve/vl} arrays store pointers to blocks of 256 data
37  * entries each.
38  *
39  * The index of an entry is >>8 (the highest 24 * bits), to find an
40  * offset in a 256-entry block.
41  *
42  * - If the 256-entry block entry has an entry in the
43  * vertnodes/bloha/blovl array of the current block, the i-th entry in
44  * that block is allocated to this entry.
45  *
46  * - If the entry has no block allocated for it yet, memory is
47  * allocated.
48  *
49  * The pointer to the correct entry is returned. Memory is guarateed
50  * to exist (as long as the malloc does not break). Since guarded
51  * allocation is used, memory _must_ be available. Otherwise, an
52  * exit(0) would occur.
53  * 
54  */
55
56 #include <limits.h>
57 #include <math.h>
58 #include <string.h>
59
60 #include "MEM_guardedalloc.h"
61 #include "BKE_utildefines.h"
62 #include "BLI_arithb.h"
63
64 #include "DNA_material_types.h" 
65 #include "DNA_mesh_types.h" 
66 #include "DNA_meshdata_types.h" 
67 #include "DNA_texture_types.h" 
68
69 #include "BKE_texture.h" 
70
71 #include "RE_render_ext.h"      /* externtex */
72
73 #include "renderpipeline.h"
74 #include "render_types.h"
75 #include "renderdatabase.h"
76 #include "texture.h"
77 #include "zbuf.h"
78
79 /* ------------------------------------------------------------------------- */
80
81 /* More dynamic allocation of options for render vertices, so we dont
82    have to reserve this space inside vertices.
83    Important; vertices should have been created already (to get tables checked) 
84    that's a reason why the calls demand VertRen * as arg, not the index */
85
86 /* NOTE! the hardcoded table size 256 is used still in code for going quickly over vertices/faces */
87
88 #define RE_STICKY_ELEMS         2
89 #define RE_STRESS_ELEMS         1
90 #define RE_RAD_ELEMS            4
91 #define RE_STRAND_ELEMS         1
92 #define RE_TANGENT_ELEMS        3
93 #define RE_STRESS_ELEMS         1
94 #define RE_WINSPEED_ELEMS               2
95
96 float *RE_vertren_get_sticky(Render *re, VertRen *ver, int verify)
97 {
98         float *sticky;
99         int nr= ver->index>>8;
100         
101         sticky= re->vertnodes[nr].sticky;
102         if(sticky==NULL) {
103                 if(verify) 
104                         sticky= re->vertnodes[nr].sticky= MEM_mallocN(256*RE_STICKY_ELEMS*sizeof(float), "sticky table");
105                 else
106                         return NULL;
107         }
108         return sticky + (ver->index & 255)*RE_STICKY_ELEMS;
109 }
110
111 float *RE_vertren_get_stress(Render *re, VertRen *ver, int verify)
112 {
113         float *stress;
114         int nr= ver->index>>8;
115         
116         stress= re->vertnodes[nr].stress;
117         if(stress==NULL) {
118                 if(verify) 
119                         stress= re->vertnodes[nr].stress= MEM_mallocN(256*RE_STRESS_ELEMS*sizeof(float), "stress table");
120                 else
121                         return NULL;
122         }
123         return stress + (ver->index & 255)*RE_STRESS_ELEMS;
124 }
125
126 /* this one callocs! */
127 float *RE_vertren_get_rad(Render *re, VertRen *ver, int verify)
128 {
129         float *rad;
130         int nr= ver->index>>8;
131         
132         rad= re->vertnodes[nr].rad;
133         if(rad==NULL) {
134                 if(verify) 
135                         rad= re->vertnodes[nr].rad= MEM_callocN(256*RE_RAD_ELEMS*sizeof(float), "rad table");
136                 else
137                         return NULL;
138         }
139         return rad + (ver->index & 255)*RE_RAD_ELEMS;
140 }
141
142 float *RE_vertren_get_strand(Render *re, VertRen *ver, int verify)
143 {
144         float *strand;
145         int nr= ver->index>>8;
146         
147         strand= re->vertnodes[nr].strand;
148         if(strand==NULL) {
149                 if(verify) 
150                         strand= re->vertnodes[nr].strand= MEM_mallocN(256*RE_STRAND_ELEMS*sizeof(float), "strand table");
151                 else
152                         return NULL;
153         }
154         return strand + (ver->index & 255)*RE_STRAND_ELEMS;
155 }
156
157 /* needs calloc */
158 float *RE_vertren_get_tangent(Render *re, VertRen *ver, int verify)
159 {
160         float *tangent;
161         int nr= ver->index>>8;
162         
163         tangent= re->vertnodes[nr].tangent;
164         if(tangent==NULL) {
165                 if(verify) 
166                         tangent= re->vertnodes[nr].tangent= MEM_callocN(256*RE_TANGENT_ELEMS*sizeof(float), "tangent table");
167                 else
168                         return NULL;
169         }
170         return tangent + (ver->index & 255)*RE_TANGENT_ELEMS;
171 }
172
173 /* needs malloc */
174 float *RE_vertren_get_winspeed(Render *re, VertRen *ver, int verify)
175 {
176         float *winspeed;
177         int nr= ver->index>>8;
178         
179         winspeed= re->vertnodes[nr].winspeed;
180         if(winspeed==NULL) {
181                 if(verify) 
182                         winspeed= re->vertnodes[nr].winspeed= MEM_mallocN(256*RE_WINSPEED_ELEMS*sizeof(float), "winspeed table");
183                 else
184                         return NULL;
185         }
186         return winspeed + (ver->index & 255)*RE_WINSPEED_ELEMS;
187 }
188
189 VertRen *RE_findOrAddVert(Render *re, int nr)
190 {
191         VertTableNode *temp;
192         VertRen *v;
193         int a;
194
195         if(nr<0) {
196                 printf("error in findOrAddVert: %d\n",nr);
197                 return NULL;
198         }
199         a= nr>>8;
200         
201         if (a>=re->vertnodeslen-1) {  /* Need to allocate more columns..., and keep last element NULL for free loop */
202                 temp= re->vertnodes;
203                 
204                 re->vertnodes= MEM_mallocN(sizeof(VertTableNode)*(re->vertnodeslen+TABLEINITSIZE) , "vertnodes");
205                 if(temp) memcpy(re->vertnodes, temp, re->vertnodeslen*sizeof(VertTableNode));
206                 memset(re->vertnodes+re->vertnodeslen, 0, TABLEINITSIZE*sizeof(VertTableNode));
207                 
208                 re->vertnodeslen+=TABLEINITSIZE; 
209                 if(temp) MEM_freeN(temp);       
210         }
211         
212         v= re->vertnodes[a].vert;
213         if(v==NULL) {
214                 int i;
215                 
216                 v= (VertRen *)MEM_callocN(256*sizeof(VertRen),"findOrAddVert");
217                 re->vertnodes[a].vert= v;
218                 
219                 for(i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++) {
220                         v[a].index= i;
221                 }
222         }
223         v+= (nr & 255);
224         return v;
225 }
226
227 void free_renderdata_vertnodes(VertTableNode *vertnodes)
228 {
229         int a;
230         
231         for(a=0; vertnodes[a].vert; a++) {
232                 MEM_freeN(vertnodes[a].vert);
233                 
234                 if(vertnodes[a].rad)
235                         MEM_freeN(vertnodes[a].rad);
236                 if(vertnodes[a].sticky)
237                         MEM_freeN(vertnodes[a].sticky);
238                 if(vertnodes[a].strand)
239                         MEM_freeN(vertnodes[a].strand);
240                 if(vertnodes[a].tangent)
241                         MEM_freeN(vertnodes[a].tangent);
242                 if(vertnodes[a].stress)
243                         MEM_freeN(vertnodes[a].stress);
244                 if(vertnodes[a].winspeed)
245                         MEM_freeN(vertnodes[a].winspeed);
246         }
247         
248         MEM_freeN(vertnodes);
249         
250 }
251
252 void free_renderdata_tables(Render *re)
253 {
254         int a=0;
255         
256         if(re->blovl) {
257                 for(a=0; re->blovl[a]; a++)
258                         MEM_freeN(re->blovl[a]);
259                 
260                 MEM_freeN(re->blovl);
261                 re->blovl= NULL;
262                 re->blovllen= 0;
263         }
264         
265         if(re->bloha) {
266                 for(a=0; re->bloha[a]; a++)
267                         MEM_freeN(re->bloha[a]);
268
269                 MEM_freeN(re->bloha);
270                 re->bloha= NULL;
271                 re->blohalen= 0;
272         }
273
274         if(re->vertnodes) {
275                 free_renderdata_vertnodes(re->vertnodes);
276                 re->vertnodes= NULL;
277                 re->vertnodeslen= 0;
278         }
279 }
280
281
282 /* ------------------------------------------------------------------------ */
283
284 HaloRen *RE_findOrAddHalo(Render *re, int nr)
285 {
286         HaloRen *h, **temp;
287         int a;
288
289         if(nr<0) {
290                 printf("error in findOrAddHalo: %d\n",nr);
291                 return NULL;
292         }
293         a= nr>>8;
294         
295         if (a>=re->blohalen-1){  /* Need to allocate more columns..., and keep last element NULL for free loop */
296                 //printf("Allocating %i more halo groups.  %i total.\n", 
297                 //      TABLEINITSIZE, re->blohalen+TABLEINITSIZE );
298                 temp=re->bloha;
299                 
300                 re->bloha=(HaloRen**)MEM_callocN(sizeof(void*)*(re->blohalen+TABLEINITSIZE) , "Bloha");
301                 if(temp) memcpy(re->bloha, temp, re->blohalen*sizeof(void*));
302                 memset(&(re->bloha[re->blohalen]), 0, TABLEINITSIZE*sizeof(void*));
303                 re->blohalen+=TABLEINITSIZE;  /*Does this really need to be power of 2?*/
304                 if(temp) MEM_freeN(temp);       
305         }
306         
307         h= re->bloha[a];
308         if(h==NULL) {
309                 h= (HaloRen *)MEM_callocN(256*sizeof(HaloRen),"findOrAdHalo");
310                 re->bloha[a]= h;
311         }
312         h+= (nr & 255);
313         return h;
314 }
315
316 /* ------------------------------------------------------------------------ */
317
318 VlakRen *RE_findOrAddVlak(Render *re, int nr)
319 {
320         VlakRen *v, **temp;
321         int a;
322
323         if(nr<0) {
324                 printf("error in findOrAddVlak: %d\n",nr);
325                 return re->blovl[0];
326         }
327         a= nr>>8;
328         
329         if (a>=re->blovllen-1){  /* Need to allocate more columns..., and keep last element NULL for free loop */
330                 // printf("Allocating %i more face groups.  %i total.\n", 
331                 //      TABLEINITSIZE, re->blovllen+TABLEINITSIZE );
332                 temp= re->blovl;
333                 
334                 re->blovl=(VlakRen**)MEM_callocN(sizeof(void*)*(re->blovllen+TABLEINITSIZE) , "Blovl");
335                 if(temp) memcpy(re->blovl, temp, re->blovllen*sizeof(void*));
336                 memset(&(re->blovl[re->blovllen]), 0, TABLEINITSIZE*sizeof(void*));
337                 re->blovllen+=TABLEINITSIZE;  /*Does this really need to be power of 2?*/
338                 if(temp) MEM_freeN(temp);       
339         }
340         
341         v= re->blovl[a];
342         
343         if(v==NULL) {
344                 v= (VlakRen *)MEM_callocN(256*sizeof(VlakRen),"findOrAddVlak");
345                 re->blovl[a]= v;
346         }
347         v+= (nr & 255);
348         return v;
349 }
350
351 /* ------------------------------------------------------------------------- */
352
353 HaloRen *RE_inithalo(Render *re, Material *ma,   float *vec,   float *vec1, 
354                                   float *orco,   float hasize,   float vectsize, int seed)
355 {
356         HaloRen *har;
357         MTex *mtex;
358         float tin, tr, tg, tb, ta;
359         float xn, yn, zn, texvec[3], hoco[4], hoco1[4];
360
361         if(hasize==0.0) return NULL;
362
363         projectverto(vec, re->winmat, hoco);
364         if(hoco[3]==0.0) return NULL;
365         if(vec1) {
366                 projectverto(vec1, re->winmat, hoco1);
367                 if(hoco1[3]==0.0) return NULL;
368         }
369
370         har= RE_findOrAddHalo(re, re->tothalo++);
371         VECCOPY(har->co, vec);
372         har->hasize= hasize;
373
374         /* actual projectvert is done in function transform_renderdata() because of parts/border/pano */
375
376         /* halovect */
377         if(vec1) {
378
379                 har->type |= HA_VECT;
380
381                 zn= hoco[3];
382                 har->xs= 0.5*re->winx*(hoco[0]/zn);
383                 har->ys= 0.5*re->winy*(hoco[1]/zn);
384                 har->zs= 0x7FFFFF*(hoco[2]/zn);
385
386                 har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn); 
387
388                 xn=  har->xs - 0.5*re->winx*(hoco1[0]/hoco1[3]);
389                 yn=  har->ys - 0.5*re->winy*(hoco1[1]/hoco1[3]);
390                 if(xn==0.0 || (xn==0.0 && yn==0.0)) zn= 0.0;
391                 else zn= atan2(yn, xn);
392
393                 har->sin= sin(zn);
394                 har->cos= cos(zn);
395                 zn= VecLenf(vec1, vec);
396
397                 har->hasize= vectsize*zn + (1.0-vectsize)*hasize;
398                 
399                 VecSubf(har->no, vec, vec1);
400                 Normalise(har->no);
401         }
402
403         if(ma->mode & MA_HALO_XALPHA) har->type |= HA_XALPHA;
404
405         har->alfa= ma->alpha;
406         har->r= ma->r;
407         har->g= ma->g;
408         har->b= ma->b;
409         har->add= (255.0*ma->add);
410         har->mat= ma;
411         har->hard= ma->har;
412         har->seed= seed % 256;
413
414         if(ma->mode & MA_STAR) har->starpoints= ma->starc;
415         if(ma->mode & MA_HALO_LINES) har->linec= ma->linec;
416         if(ma->mode & MA_HALO_RINGS) har->ringc= ma->ringc;
417         if(ma->mode & MA_HALO_FLARE) har->flarec= ma->flarec;
418
419
420         if(ma->mtex[0]) {
421
422                 if( (ma->mode & MA_HALOTEX) ) har->tex= 1;
423                 else {
424
425                         mtex= ma->mtex[0];
426                         VECCOPY(texvec, vec);
427
428                         if(mtex->texco & TEXCO_NORM) {
429                                 ;
430                         }
431                         else if(mtex->texco & TEXCO_OBJECT) {
432                                 /* texvec[0]+= imatbase->ivec[0]; */
433                                 /* texvec[1]+= imatbase->ivec[1]; */
434                                 /* texvec[2]+= imatbase->ivec[2]; */
435                                 /* Mat3MulVecfl(imatbase->imat, texvec); */
436                         }
437                         else {
438                                 if(orco) {
439                                         VECCOPY(texvec, orco);
440                                 }
441                         }
442
443                         externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta);
444
445                         yn= tin*mtex->colfac;
446                         zn= tin*mtex->varfac;
447
448                         if(mtex->mapto & MAP_COL) {
449                                 zn= 1.0-yn;
450                                 har->r= (yn*tr+ zn*ma->r);
451                                 har->g= (yn*tg+ zn*ma->g);
452                                 har->b= (yn*tb+ zn*ma->b);
453                         }
454                         if(mtex->texco & 16) {
455                                 har->alfa= tin;
456                         }
457                 }
458         }
459
460         return har;
461 }
462
463 /* -------------------------- operations on entire database ----------------------- */
464
465 static float get_pano_rot(Render *re, int part)
466 {
467         static float alpha= 1.0;
468
469         /* part==0 init all */
470         if(part==0) {
471                 alpha= ((float)re->r.xsch)/re->viewfac;
472                 alpha= 2.0*atan(alpha/2.0);
473         }
474         
475         /* rotate it all around the y-as with phi degrees */
476         return 0.5*(re->r.xparts-1)*alpha + part*alpha;
477 }
478
479 /* ugly function for halos in panorama */
480 static int panotestclip(Render *re, int do_pano, float *v)
481 {
482         /* to be used for halos en infos */
483         float abs4;
484         short c=0;
485
486         if(do_pano) return testclip(v);
487
488         abs4= fabs(v[3]);
489
490         if(v[2]< -abs4) c=16;           /* this used to be " if(v[2]<0) ", see clippz() */
491         else if(v[2]> abs4) c+= 32;
492
493         if( v[1]>abs4) c+=4;
494         else if( v[1]< -abs4) c+=8;
495
496         abs4*= re->r.xparts;
497         if( v[0]>abs4) c+=2;
498         else if( v[0]< -abs4) c+=1;
499
500         return c;
501 }
502
503 /*
504   This adds the hcs coordinates to vertices. It iterates over all
505   vertices, halos and faces. After the conversion, we clip in hcs.
506
507   Elsewhere, all primites are converted to vertices. 
508   Called in 
509   - envmapping (envmap.c)
510   - shadow buffering (shadbuf.c)
511 */
512
513 void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], float *),  int do_pano, int part)
514 {
515         VlakRen *vlr = NULL;
516         VertRen *ver = NULL;
517         HaloRen *har = NULL;
518         float zn, vec[3], hoco[4];
519         int a;
520         
521         if(do_pano) {
522                 float panophi= get_pano_rot(re, part);
523                 
524                 re->panosi= sin(panophi);
525                 re->panoco= cos(panophi);
526         }
527         
528    /* calculate view coordinates (and zbuffer value) */
529         for(a=0; a< re->totvert;a++) {
530                 if((a & 255)==0) ver= RE_findOrAddVert(re, a);
531                 else ver++;
532
533                 if(do_pano) {
534                         vec[0]= re->panoco*ver->co[0] + re->panosi*ver->co[2];
535                         vec[1]= ver->co[1];
536                         vec[2]= -re->panosi*ver->co[0] + re->panoco*ver->co[2];
537                 }
538                 else {
539                         VECCOPY(vec, ver->co);
540                 }
541                 /* Go from wcs to hcs ... */
542                 projectfunc(vec, re->winmat, ver->ho);
543                 /* ... and clip in that system. */
544                 ver->clip = testclip(ver->ho);
545                 /* 
546                    Because all other ops are performed in other systems, this is 
547                    the only thing that has to be done.
548                 */
549         }
550
551    /* calculate view coordinates (and zbuffer value) */
552         for(a=0; a<re->tothalo; a++) {
553                 if((a & 255)==0) har= re->bloha[a>>8];
554                 else har++;
555
556                 if(do_pano) {
557                         vec[0]= re->panoco*har->co[0] + re->panosi*har->co[2];
558                         vec[1]= har->co[1];
559                         vec[2]= -re->panosi*har->co[0] + re->panoco*har->co[2];
560                 }
561                 else {
562                         VECCOPY(vec, har->co);
563                 }
564
565                 projectfunc(vec, re->winmat, hoco);
566                 
567                 /* we clip halos less critical, but not for the Z */
568                 hoco[0]*= 0.5;
569                 hoco[1]*= 0.5;
570                 
571                 if( panotestclip(re, do_pano, hoco) ) {
572                         har->miny= har->maxy= -10000;   /* that way render clips it */
573                 }
574                 else if(hoco[3]<0.0) {
575                         har->miny= har->maxy= -10000;   /* render clips it */
576                 }
577                 else /* do the projection...*/
578                 {
579                         /* bring back hocos */
580                         hoco[0]*= 2.0;
581                         hoco[1]*= 2.0;
582                         
583                         zn= hoco[3];
584                         har->xs= 0.5*re->winx*(1.0+hoco[0]/zn); /* the 0.5 negates the previous 2...*/
585                         har->ys= 0.5*re->winy*(1.0+hoco[1]/zn);
586                 
587                         /* this should be the zbuffer coordinate */
588                         har->zs= 0x7FFFFF*(hoco[2]/zn);
589                         /* taking this from the face clip functions? seems ok... */
590                         har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn);
591                         
592                         vec[0]+= har->hasize;
593                         projectfunc(vec, re->winmat, hoco);
594                         vec[0]-= har->hasize;
595                         zn= hoco[3];
596                         har->rad= fabs(har->xs- 0.5*re->winx*(1.0+hoco[0]/zn));
597                 
598                         /* this clip is not really OK, to prevent stars to become too large */
599                         if(har->type & HA_ONLYSKY) {
600                                 if(har->rad>3.0) har->rad= 3.0;
601                         }
602                 
603                         har->radsq= har->rad*har->rad;
604                 
605                         har->miny= har->ys - har->rad/re->ycor;
606                         har->maxy= har->ys + har->rad/re->ycor;
607                 
608                         /* the Zd value is still not really correct for pano */
609                 
610                         vec[2]-= har->hasize;   /* z negative, otherwise it's clipped */
611                         projectfunc(vec, re->winmat, hoco);
612                         zn= hoco[3];
613                         zn= fabs( (float)har->zs - 0x7FFFFF*(hoco[2]/zn));
614                         har->zd= CLAMPIS(zn, 0, INT_MAX);
615                 
616                 }
617                 
618         }
619
620         /* set flags at 0 if clipped away */
621         for(a=0; a<re->totvlak; a++) {
622                 if((a & 255)==0) vlr= re->blovl[a>>8];
623                 else vlr++;
624
625                         vlr->flag |= R_VISIBLE;
626                         if(vlr->v4) {
627                                 if(vlr->v1->clip & vlr->v2->clip & vlr->v3->clip & vlr->v4->clip) vlr->flag &= ~R_VISIBLE;
628                         }
629                         else if(vlr->v1->clip & vlr->v2->clip & vlr->v3->clip) vlr->flag &= ~R_VISIBLE;
630
631                 }
632
633 }
634
635 /* ------------------------------------------------------------------------- */
636
637 void set_normalflags(Render *re)
638 {
639         VlakRen *vlr = NULL;
640         float *v1, xn, yn, zn;
641         int a1, doflip;
642         
643         /* switch normal 'snproj' values (define which axis is the optimal one for calculations) */
644         for(a1=0; a1<re->totvlak; a1++) {
645                 if((a1 & 255)==0) vlr= re->blovl[a1>>8];
646                 else vlr++;
647                 
648                 /* abuse of this flag... this is code that just sets face normal in direction of camera */
649                 /* that convention we should get rid of */
650                 if((vlr->flag & R_NOPUNOFLIP)==0) {
651                         
652                         doflip= 0;
653                         if(re->r.mode & R_ORTHO) {
654                                 if(vlr->n[2]>0.0) doflip= 1;
655                         }
656                         else {
657                                 v1= vlr->v1->co;
658                                 if( (v1[0]*vlr->n[0] +v1[1]*vlr->n[1] +v1[2]*vlr->n[2])<0.0 ) doflip= 1;
659                         }
660                         if(doflip) {
661                                 vlr->n[0]= -vlr->n[0];
662                                 vlr->n[1]= -vlr->n[1];
663                                 vlr->n[2]= -vlr->n[2];
664                         }
665                 }
666                 
667                 /* recalculate puno. Displace & flipped matrices can screw up */
668                 vlr->puno= 0;
669                 if(!(vlr->flag & R_TANGENT)) {
670                         if( Inpf(vlr->n, vlr->v1->n) < 0.0 ) vlr->puno |= ME_FLIPV1;
671                         if( Inpf(vlr->n, vlr->v2->n) < 0.0 ) vlr->puno |= ME_FLIPV2;
672                         if( Inpf(vlr->n, vlr->v3->n) < 0.0 ) vlr->puno |= ME_FLIPV3;
673                         if(vlr->v4 && Inpf(vlr->n, vlr->v4->n) < 0.0 ) vlr->puno |= ME_FLIPV4;
674                 }                               
675                 xn= fabs(vlr->n[0]);
676                 yn= fabs(vlr->n[1]);
677                 zn= fabs(vlr->n[2]);
678                 if(zn>=xn && zn>=yn) vlr->snproj= 0;
679                 else if(yn>=xn && yn>=zn) vlr->snproj= 1;
680                 else vlr->snproj= 2;
681
682         }
683 }
684
685
686