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