Bunch of gcc 4.0 warning fixes.
[blender.git] / source / blender / render / intern / source / edgeRender.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  * Add enhanced edges on a rendered image (toon shading, edge shading).
32  */
33
34 /*
35  * Edge rendering: use a mask to weigh the depth of neighbouring
36  * pixels, and do a colour correction.
37  *
38  * We need:
39  * - a buffer to store the depths (ints)
40  * - a function that alters the colours in R.rectot (copy edge_enhance?)
41  *   The max. z buffer depth is 0x7FFF.FFFF (7 F's)
42  *
43  * - We 'ignore' the pixels falling outside the regular buffer (we fill)
44  *   these with the max depth. This causes artefacts when rendering in
45  *   parts.
46  */
47
48 /* ------------------------------------------------------------------------- */
49
50 /* enable extra bounds checking and tracing                                  */
51 /* #define RE_EDGERENDERSAFE */
52 /* disable the actual edge correction                                        */
53 /*  #define RE_EDGERENDER_NO_CORRECTION */
54
55 #include <stdlib.h>
56 #include <math.h>
57 #include <string.h>
58 #include <limits.h>       /* INT_MIN,MAX are used here                       */ 
59 #include <stdio.h>
60
61 #include "MEM_guardedalloc.h"
62 #include "MTC_vectorops.h"
63 #include "BKE_utildefines.h"
64
65 #include "RE_callbacks.h"
66 #include "edgeRender.h"
67 #include "render.h"
68 #include "zbuf.h" /* for zbufclipwire and zbufclip */
69 #include "jitter.h"
70
71 #ifdef RE_EDGERENDERSAFE
72 char edgeRender_h[] = EDGERENDER_H;
73 char edgeRender_c[] = "$Id$";
74 #include "errorHandler.h"
75 #endif
76
77 /* ------------------------------------------------------------------------- */
78
79  /* These function pointers are used for z buffer filling.    */
80 extern void (*zbuffunc)(unsigned int, float *, float *, float *);
81 extern void (*zbuflinefunc)(unsigned int, float *, float *); 
82 extern float Zmulx, Zmuly;   /* Some kind of scale?                          */
83 extern float Zjitx,Zjity;    /* The x,y values for jitter offset             */
84
85 /* ------------------------------------------------------------------------- */
86
87 /* exp: */
88 static Material** matBuffer;  /* buffer with material indices                */
89 static Material* mat_cache;  /* material of the face being buffered          */
90
91 static char* colBuffer;      /* buffer with colour correction                */
92 static int *edgeBuffer;      /* buffer with distances                        */
93 static int  bufWidth;        /* x-dimension of the buffer                    */
94 static int  bufHeight;       /* y-dimension of the buffer                    */
95 static int  imWidth;         /* x-dimension of the image                     */
96 static int  imHeight;        /* y-dimension of the image                     */
97 static int  osaCount;        /* oversample count                             */
98 static int  maskBorder;      /* size of the mask border                      */
99 static short int intensity;  /* edge intensity                               */
100 static short int same_mat_redux; /* intensity reduction for boundaries with the same material */
101 static int  compatible_mode; /* edge positioning compatible with old rederer */
102 static int  selectmode;      /* 0: only solid faces, 1: also transparent f's */
103
104 static int  Aminy;           /* y value of first line in the accu buffer     */
105 static int  Amaxy;           /* y value of last line in the accu buffer      */
106                              /* -also used to clip when zbuffering           */
107 static char edgeR;           /* Colour for the edges. The edges will receive */ 
108 static char edgeG;           /* this tint. The colour is fully used!         */
109 static char edgeB;
110 /*  static float edgeBlend;  */     /* Indicate opaqueness of the edge colour.      */
111
112 /* Local functions --------------------------------------------------------- */
113 /**
114  * Initialise the edge render buffer memory.
115  */
116 void initEdgeRenderBuffer(void);
117 /**
118  * Release buffer memory.
119  */
120 void freeEdgeRenderBuffer(void);
121
122 /**
123  * Set all distances in the distance buffer to the maximum distance.
124  */
125 void resetDistanceBuffer(void);
126
127 /**
128  * Insert this distance at these pixel coordinates.
129  */
130 void insertInEdgeBuffer(int x, int y, int dist);
131
132 /**
133  * Renders enhanced edges. Distances from distRect are used to
134  * determine a correction on colourRect
135  */
136 void renderEdges(char * colourRect);
137
138 /**
139  * Buffer an edge between these two vertices in the e.r. distance buffer.
140  */
141 static void fillEdgeRenderEdge(unsigned int, float *vec1, float *vec2);
142
143 /**
144  * Buffer a face between these two vertices in the e.r. distance buffer.
145  */
146 static void fillEdgeRenderFace(unsigned int, float *v1, float *v2, float *v3);
147
148 /**
149  * Compose the edge render colour buffer.
150  */
151 void calcEdgeRenderColBuf(char * tarbuf);
152
153 /**
154  * Loop over all objects that need to be edge rendered. This loop determines
155  * which objects get to be elected for edge rendering.
156  */
157 int  zBufferEdgeRenderObjects(void);
158
159 /**
160  * Add edge pixels to the original image. It blends <bron> over <doel>.
161  */
162 void addEdgeOver(unsigned char *dst, unsigned char *src);
163
164 /* ------------------------------------------------------------------------- */
165
166 void addEdges(
167         char * targetbuf,
168         int iw, int ih,
169         int osanr,
170         short int intens, short int intens_redux,
171         int compat, int mode,
172         float r, float g, float b
173         )
174 {
175         float rf, gf ,bf;
176         /* render parameters */
177         selectmode = mode;
178         imWidth    = iw;
179         imHeight   = ih;
180         compatible_mode = compat;
181         osaCount   = osanr;
182         intensity  = intens;
183         /* Reduction doesn't exceed intensity. */
184         same_mat_redux = ((intens_redux < intensity)? intens_redux : intensity);
185         
186         rf      = r * 255.0;
187         if (rf > 255) edgeR = 255; else edgeR = rf;
188         gf      = g * 255.0;
189         if (gf > 255) edgeG = 255; else edgeG = gf;
190         bf      = b * 255.0;
191         if (bf > 255) edgeB = 255; else edgeB = bf;
192
193         /* Go! */
194         initEdgeRenderBuffer();
195         calcEdgeRenderColBuf(targetbuf);
196         freeEdgeRenderBuffer();
197         
198 } /* end of void addEdges(char *, int, int, int, short int , int) */
199
200 /* ------------------------------------------------------------------------- */
201
202 void initEdgeRenderBuffer()
203 {
204         char *ptr;
205         int i;
206         
207         maskBorder = 1; /* for 3 by 3 mask*/
208                 
209         bufWidth   = imWidth + (2 * maskBorder);
210         bufHeight  = imHeight + (2 * maskBorder);
211         
212         /* Experimental: store the material indices. */
213         if (same_mat_redux) {
214                 matBuffer  = MEM_callocN(sizeof(Material*)
215                                          * bufWidth * bufHeight, "matBuffer");
216         }
217         
218         edgeBuffer = MEM_callocN(sizeof(int) * bufWidth * bufHeight, "edgeBuffer");
219         colBuffer  = MEM_callocN(sizeof(char) * 4 * imWidth * imHeight, "colBuffer");
220
221         
222         if ((edgeR != 0) || (edgeG != 0) || (edgeB != 0)) {
223                 /* Set all colbuf pixels to the edge colour. Leave alpha channel     */
224                 /* cleared. Actually, we could blend in any image here...            */
225                 ptr = colBuffer;
226                 for (i = 0; i < imWidth * imHeight; i++, ptr+=4)
227                 {
228                         ptr[0] = edgeR;
229                         ptr[1] = edgeG;
230                         ptr[2] = edgeB;
231                         ptr[3] = 0;
232                 }
233         }
234         
235 #ifdef RE_EDGERENDERSAFE
236         if (!edgeBuffer || !colBuffer) {
237                 char *fname = "initEdgeRenderBuffer";
238                 RE_error(RE_CANNOT_ALLOCATE_MEMORY, fname);
239         }
240 #endif
241 } /* end of void initEdgeRenderBuffer(void) */
242
243 /* ------------------------------------------------------------------------- */
244 void freeEdgeRenderBuffer(void)
245 {
246         if(edgeBuffer) MEM_freeN(edgeBuffer);
247         edgeBuffer= NULL;
248         if(colBuffer)  MEM_freeN(colBuffer);
249         colBuffer= NULL;
250         if(matBuffer)  MEM_freeN(matBuffer);
251         matBuffer= NULL;
252 } /* end of void freeEdgeRenderBuffer(void) */
253
254 /* ------------------------------------------------------------------------- */
255
256 void resetDistanceBuffer(void)
257 {
258         int i;
259         for(i = 0; i < bufWidth * bufHeight; i++) edgeBuffer[i] = 0x7FFFFFFF;
260 } /* end of void resetDistanceBuffer(void) */
261
262 /* ------------------------------------------------------------------------- */
263
264 void insertInEdgeBuffer(int x, int y, int dist)
265 {
266         int index;
267 #ifdef RE_EDGERENDERSAFE
268         char *fname = "insertInEdgeBuffer";
269         if ((x < 0) || (x > imWidth ) ||
270                 (y < 0) || (y > (imHeight-1) ) ) {
271                 RE_error(RE_EDGERENDER_WRITE_OUTSIDE_BUFFER, fname);
272                 return;
273         }
274 #endif
275
276         /* +1? */
277         index = (y * bufWidth) + x + maskBorder;
278
279         /*exp: just dump a single index here. Maybe we can do more
280          * sophisticated things later on. */
281         if (same_mat_redux) {
282                 matBuffer[index] = mat_cache;
283         }
284         
285         if (edgeBuffer[index] >dist ) edgeBuffer[index] = dist;
286
287 } /* end of void insertInEdgeBuffer(int x, int y, int dist) */
288
289 /* ------------------------------------------------------------------------- */
290 /* Modelled after rendercore.c/edge_enhance()                                */
291 void renderEdges(char *colourRect)
292 {
293         /* use zbuffer to define edges, add it to the image */
294         int val, y, x, col, *rz, *rz1, *rz2, *rz3;
295         char *cp;
296         int targetoffset, heightoffset;
297         int i;
298         int matdif; /* For now: just a bogus int, 0 when all materials
299                      * under the mask are the same, non-0 otherwise*/
300         int *matptr_low = 0, *matptr_cent = 0, *matptr_high = 0;
301         int matdiffac = 0;
302
303 #ifdef RE_EDGERENDER_NO_CORRECTION
304         return; /* no edge correction */
305 #endif
306         
307 #ifdef RE_EDGERENDERSAFE
308     fprintf(stderr, "\n*** Activated full error trace on "
309             "edge rendering  using:\n\t%s\n\t%s"
310                         "\n*** Rendering edges at %d intensity", 
311             edgeRender_c, edgeRender_h, intensity);
312 #endif
313
314         
315         /* Old renderer uses wrong positions! With the compat switch on, the po- */
316         /* sitions will be corrected to be offset in the same way.               */
317         if (compatible_mode) {
318                 targetoffset = 4 * (imWidth - 1);
319                 heightoffset = -1;
320         } else {
321                 targetoffset = 0;
322                 heightoffset = 0;
323         }
324         
325         /* Fill edges with some default values. We just copy what is in the edge */
326         /* This looks messy, but it appears to be ok.                            */
327         edgeBuffer[0]                          = edgeBuffer[bufWidth + 1];
328         edgeBuffer[bufWidth - 1]               = edgeBuffer[(2 * bufWidth) - 2];
329         edgeBuffer[bufWidth * (bufHeight - 1)] =
330                 edgeBuffer[bufWidth * (bufHeight - 2) + 1];
331         edgeBuffer[(bufWidth * bufHeight) - 1] =
332                 edgeBuffer[(bufWidth * (bufHeight - 1)) - 2];
333         for (i = 1; i < bufWidth - 1; i++) { /* lieing edges */
334                 edgeBuffer[i] = edgeBuffer[bufWidth + i]; /* bottom*/
335                 edgeBuffer[((bufHeight - 1)*bufWidth) + i]
336                         = edgeBuffer[((bufHeight - 2)*bufWidth) + i]; /* top */
337         }
338         for (i = 1; i < bufHeight - 2; i++) { /* standing edges */
339                 edgeBuffer[i * bufWidth] = edgeBuffer[(i * bufWidth) + 1]; /* left */
340                 edgeBuffer[((i + 1) * bufWidth) - 1] =
341                         edgeBuffer[((i + 1) * bufWidth) - 2]; /* right */
342         }
343
344         /* same hack for the materials: */
345         if (same_mat_redux) {
346                 matBuffer[0]                          = matBuffer[bufWidth + 1];
347                 matBuffer[bufWidth - 1]               = matBuffer[(2 * bufWidth) - 2];
348                 matBuffer[bufWidth * (bufHeight - 1)] =
349                         matBuffer[bufWidth * (bufHeight - 2) + 1];
350                 matBuffer[(bufWidth * bufHeight) - 1] =
351                         matBuffer[(bufWidth * (bufHeight - 1)) - 2];
352                 for (i = 1; i < bufWidth - 1; i++) { /* lieing mats */
353                         matBuffer[i] = matBuffer[bufWidth + i]; /* bottom*/
354                         matBuffer[((bufHeight - 1)*bufWidth) + i]
355                                 = matBuffer[((bufHeight - 2)*bufWidth) + i]; /* top */
356                 }
357                 for (i = 1; i < bufHeight - 2; i++) { /* standing mats */
358                         matBuffer[i * bufWidth] = matBuffer[(i * bufWidth) + 1]; /* left */
359                         matBuffer[((i + 1) * bufWidth) - 1] =
360                                 matBuffer[((i + 1) * bufWidth) - 2]; /* right */
361                 }
362         }
363                 
364         /* shift values in zbuffer 3 to the right */
365         rz = edgeBuffer;
366         if(rz==0) return;
367         
368         for(y=0; y < bufHeight * bufWidth; y++, rz++) {
369                 (*rz)>>= 3;
370         }
371         
372         /* Distance pointers */
373         rz1= edgeBuffer;
374         rz2= rz1 + bufWidth;
375         rz3= rz2 + bufWidth;
376
377         if (same_mat_redux) {
378                 matptr_low = (int *) matBuffer;
379                 matptr_cent = matptr_low + bufWidth;
380                 matptr_high = matptr_cent + bufWidth;
381         }
382         
383         if (osaCount == 1) {
384                 cp = colourRect + targetoffset;
385         } else {
386                 cp = colBuffer + targetoffset;
387         }
388
389         i = 0;
390         
391         for(y = 0; y < (imHeight + heightoffset) ; y++) {
392
393
394                 /* All these indices are a bit silly. I need to
395                  * rewrite this, so all buffers use the same
396                  * indexing. */
397                 for(x = 0;
398                     x < imWidth;
399                     x++, rz1++, rz2++, rz3++, cp+=4,
400                             matptr_low++,
401                             matptr_cent++,
402                             matptr_high++) {
403                         
404                         col= abs( -   rz1[0] -  2*rz1[1] -   rz1[2]
405                                   - 2*rz2[0] + 12*rz2[1] - 2*rz2[2]
406                                   -   rz3[0] -  2*rz3[1] -   rz3[2]) / 3;
407
408                         /* Several options for matdif:
409                          *
410                          * - suppress all boundaries with 0 dif
411                          *
412                          * - weaken col dif? Or decrease intensity by
413                          * a factor when non 0 dif??
414                          */
415
416                         /* exp: matdif is non-0 if the mask-center
417                          * material differs from any of the
418                          * corners. */
419
420                         if (same_mat_redux) {
421                                 matdif = abs (matptr_cent[1] - matptr_low[0])
422                                         + abs (matptr_cent[1] - matptr_low[1])
423                                         + abs (matptr_cent[1] - matptr_low[2])
424                                         + abs (matptr_cent[1] - matptr_cent[0])
425                                         + abs (matptr_cent[1] - matptr_low[2])
426                                         + abs (matptr_cent[1] - matptr_high[0])
427                                         + abs (matptr_cent[1] - matptr_high[1])
428                                         + abs (matptr_cent[1] - matptr_high[2]);
429                                 
430                                 matdiffac = (matdif ? 0 : same_mat_redux); 
431                         }
432                         
433                         col= ((intensity - matdiffac) * col)>>14;
434                         if(col>255) col= 255;
435                         
436                         /* Colour edge if
437                          *
438                          * 1. there is an appreciable, non-uniform
439                          * gradient,
440                          *
441                          * 2. there are different materials bordering
442                          * on the center pixel
443                          */
444                         if( (col>0)
445                             /* && (matdif != 0) */) {
446                                 
447                                 if(osaCount > 1) {
448                                         /* Currently done by tweaking alpha. The colBuffer is     */
449                                         /* filled with pixels of the colour appropriate for the   */
450                                         /* edges. This colour is alpha-blended over the image.    */
451                                         /* This calculation determines how much colour each pixel */
452                                         /* gets.                                                  */
453                                         col/= osaCount;
454                                         val= cp[3]+col;
455                                         if(val>255) cp[3]= 255; else cp[3]= val;
456                                 }
457                                 else {
458                                         /* the pixel is blackened when col is too big */
459                                         val = cp[0] - col;
460                                         if(val<=0) {
461                                                 cp[0]= edgeR;
462                                         } else {
463                                                 cp[0]= val;
464                                         }
465                                         val = cp[1] - col;
466                                         if(val<=0) {
467                                                 cp[1]= edgeG;
468                                         }else {
469                                                 cp[1]= val;
470                                         }
471                                         val = cp[2] - col;
472                                         if(val<=0) {
473                                                 cp[2]= edgeB;
474                                         } else {
475                                                 cp[2]= val;
476                                         }
477                                 }
478                         }
479                 }
480                 rz1+= 2;
481                 rz2+= 2;
482                 rz3+= 2;
483                 if (same_mat_redux) {
484                         matptr_low += 2;
485                         matptr_cent += 2;
486                         matptr_high += 2;
487                 }
488                 
489         }
490
491 } /* end of void renderEdges() */
492
493 /* ------------------------------------------------------------------------- */
494
495 /* adds src to dst */
496 void addEdgeOver(unsigned char *dst, unsigned char *src)   
497 {
498    unsigned char inverse;
499    unsigned char alpha;
500    unsigned int  c;
501
502    alpha = src[3];
503   
504    if( alpha == 0) return;
505    if( alpha == 255) { 
506       /* when full opacity, just copy the pixel */
507       /* this code assumes an int is 32 bit, fix 
508
509       *(( unsigned int *)dst)= *((unsigned int *)src);
510         replaced with memcpy
511                 */
512        memcpy(dst,src,4);
513        return;
514    }
515   
516    /* This must be a special blend-mode, because we get a 'weird' data      */
517    /* input format now. With edge = (c_e, a_e), picture = (c_p, a_p), we    */
518    /* get: result = ( c_e*a_e + c_p(1 - a_e), a_p ).                        */
519   
520    inverse = 255 - alpha;
521   
522    c = ((unsigned int)inverse * (unsigned int) dst[0] + (unsigned int)src[0] *
523         (unsigned int)alpha) >> 8;
524    dst[0] = c;
525
526    c = ((unsigned int)inverse * (unsigned int) dst[1] + (unsigned int)src[1] *
527         (unsigned int)alpha) >> 8;
528    dst[1] = c;
529    c = ((unsigned int)inverse * (unsigned int) dst[2] + (unsigned int)src[2] *
530         (unsigned int)alpha) >> 8;
531    dst[2] = c;
532 }
533
534 void calcEdgeRenderColBuf(char* colTargetBuffer)
535 {
536
537     int keepLooping = 1;
538         int sample;
539         
540         /* zbuffer fix: here? */
541         Zmulx= ((float) imWidth)/2.0;
542         Zmuly= ((float) imHeight)/2.0;
543         
544         /* use these buffer fill functions */    
545         zbuffunc     = fillEdgeRenderFace;
546         zbuflinefunc = fillEdgeRenderEdge;
547
548         /* always buffer the max. extent */
549         Aminy = 0;
550         Amaxy = imHeight;
551                 
552         sample = 0; /* Zsample is used internally !                         */
553         while ( (sample < osaCount) && keepLooping ) {
554                 /* jitter */
555                 Zjitx= -jit[sample][0];
556                 Zjity= -jit[sample][1];
557
558                 /* should reset dis buffer here */
559                 resetDistanceBuffer();
560                 
561                 /* kick all into a z buffer */
562                 keepLooping = zBufferEdgeRenderObjects();
563
564                 /* do filtering */
565                 renderEdges(colTargetBuffer);
566
567                 if(RE_local_test_break()) keepLooping = 0; 
568                 sample++; 
569         }
570
571         /* correction for osa-sampling...*/
572         if( osaCount != 1) {
573                 unsigned char *rp, *rt;
574                 int a;
575                 
576                 rt= colTargetBuffer;
577                 rp= colBuffer;
578                 for(a = imWidth * imHeight; a>0; a--, rt+=4, rp+=4) {
579                         /* there seem to be rounding errors here... */
580                         addEdgeOver(rt, rp);
581                 }
582         }
583         
584 } /*End of void calcEdgeRenderZBuf(void) */
585
586 /* ------------------------------------------------------------------------- */
587 /* Clip flags etc. should still be set. When called in the span of 'normal'  */
588 /* rendering, this should be ok.                                             */
589 int zBufferEdgeRenderObjects(void)
590 {
591         VlakRen *vlr= NULL;
592         unsigned int zvlnr;
593     int keepLooping; 
594     int faceCounter; /* counter for face number */
595     Material *ma;
596         
597     keepLooping = 1;
598     ma          = NULL;
599     faceCounter = 0;
600                         
601     while ( (faceCounter < R.totvlak) && keepLooping) {
602             if((faceCounter & 255)==0) { vlr= R.blovl[faceCounter>>8]; }
603             else vlr++;
604         
605             ma= vlr->mat;
606
607             /*exp*/
608             mat_cache = ma;
609
610             /* face number is used in the fill functions */
611             zvlnr = faceCounter + 1; 
612         
613             if(vlr->flag & R_VISIBLE) {
614                         
615                     /* here we cull all transparent faces if mode == 0 */
616                     if (selectmode || !(ma->mode & MA_ZTRA)) {
617                             /* here we can add all kinds of extra selection criteria */
618                             if(ma->mode & (MA_WIRE)) zbufclipwire(zvlnr, vlr);
619                             else {
620                                     zbufclip(zvlnr, vlr->v1->ho,   vlr->v2->ho,   vlr->v3->ho, 
621                                              vlr->v1->clip, vlr->v2->clip, vlr->v3->clip);
622                                     if(vlr->v4) {
623                                             zvlnr+= 0x800000; /* in a sense, the 'adjoint' face */
624                                             zbufclip(zvlnr, vlr->v1->ho,   vlr->v3->ho,   vlr->v4->ho, 
625                                                      vlr->v1->clip, vlr->v3->clip, vlr->v4->clip);
626                                     }
627                             }
628                     }
629             };
630             if(RE_local_test_break()) keepLooping = 0; 
631             faceCounter++;
632     }
633     return keepLooping;
634 } /* End of int zBufferEdgeRenderObjects(void) */
635
636 /* ------------------------------------------------------------------------- */
637
638 static void fillEdgeRenderFace(unsigned int zvlnr, float *v1, float *v2, float *v3)  
639 {
640         /* Coordinates of the vertices are specified in ZCS */
641         double z0; /* used as temp var*/
642         double xx1;
643         double zxd,zyd,zy0, tmp;
644         float *minv,*maxv,*midv;
645         int zverg,x;
646         int my0,my2,sn1,sn2,rectx,zd;
647         int y,omsl,xs0,xs1,xs2,xs3, dx0,dx1,dx2/*  , mask */;
648         int linex, liney, xoffset, yoffset; /* pointers to the pixel number */
649
650         /* These used to be doubles.  We may want to change them back if the     */
651         /* loss of accuracy proves to be a problem? There does not seem to be    */
652         /* any performance issues here, so I'll just keep the doubles.           */
653         /*      float vec0[3], vec1[3], vec2[3]; */
654         double vec0[3], vec1[3], vec2[3];
655
656         /* MIN MAX */
657         /* sort vertices for min mid max y value */
658         if(v1[1]<v2[1]) {
659                 if(v2[1]<v3[1])      { minv=v1; midv=v2; maxv=v3;}
660                 else if(v1[1]<v3[1]) { minv=v1; midv=v3; maxv=v2;}
661                 else                 { minv=v3; midv=v1; maxv=v2;}
662         }
663         else {
664                 if(v1[1]<v3[1])          { minv=v2; midv=v1; maxv=v3;}
665                 else if(v2[1]<v3[1]) { minv=v2; midv=v3; maxv=v1;}
666                 else                 { minv=v3; midv=v2; maxv=v1;}
667         }
668
669         if(minv[1] == maxv[1]) return;  /* security, for 'zero' size faces */
670
671         my0  = ceil(minv[1]);
672         my2  = floor(maxv[1]);
673         omsl = floor(midv[1]);
674
675         /* outside the current z buffer slice: clip whole face */
676         if( (my2 < Aminy) || (my0 > Amaxy)) return;
677
678         if(my0<Aminy) my0= Aminy;
679
680         /* EDGES : THE LONGEST */
681         xx1= maxv[1]-minv[1];
682         if(xx1>2.0/65536.0) {
683                 z0= (maxv[0]-minv[0])/xx1;
684                 
685                 tmp= (-65536.0*z0);
686                 dx0= CLAMPIS(tmp, INT_MIN, INT_MAX);
687                 
688                 tmp= 65536.0*(z0*(my2-minv[1])+minv[0]);
689                 xs0= CLAMPIS(tmp, INT_MIN, INT_MAX);
690         }
691         else {
692                 dx0= 0;
693                 xs0= 65536.0*(MIN2(minv[0],maxv[0]));
694         }
695         /* EDGES : THE TOP ONE */
696         xx1= maxv[1]-midv[1];
697         if(xx1>2.0/65536.0) {
698                 z0= (maxv[0]-midv[0])/xx1;
699                 
700                 tmp= (-65536.0*z0);
701                 dx1= CLAMPIS(tmp, INT_MIN, INT_MAX);
702                 
703                 tmp= 65536.0*(z0*(my2-midv[1])+midv[0]);
704                 xs1= CLAMPIS(tmp, INT_MIN, INT_MAX);
705         }
706         else {
707                 dx1= 0;
708                 xs1= 65536.0*(MIN2(midv[0],maxv[0]));
709         }
710         /* EDGES : THE BOTTOM ONE */
711         xx1= midv[1]-minv[1];
712         if(xx1>2.0/65536.0) {
713                 z0= (midv[0]-minv[0])/xx1;
714                 
715                 tmp= (-65536.0*z0);
716                 dx2= CLAMPIS(tmp, INT_MIN, INT_MAX);
717                 
718                 tmp= 65536.0*(z0*(omsl-minv[1])+minv[0]);
719                 xs2= CLAMPIS(tmp, INT_MIN, INT_MAX);
720         }
721         else {
722                 dx2= 0;
723                 xs2= 65536.0*(MIN2(minv[0],midv[0]));
724         }
725
726         /* ZBUF DX DY */
727         MTC_diff3DFF(vec1, v1, v2);
728         MTC_diff3DFF(vec2, v2, v3);
729         MTC_cross3Double(vec0, vec1, vec2);
730
731         /* cross product of two of the sides is 0 => this face is too small */
732         if(vec0[2]==0.0) return;
733
734         if(midv[1] == maxv[1]) omsl= my2;
735         if(omsl < Aminy) omsl= Aminy-1;  /* that way it does the first loop entirely */
736
737         while (my2 > Amaxy) {  /* my2 can really be larger */
738                 xs0+=dx0;
739                 if (my2<=omsl) {
740                         xs2+= dx2;
741                 }
742                 else{
743                         xs1+= dx1;
744                 }
745                 my2--;
746         }
747
748         xx1= (vec0[0]*v1[0]+vec0[1]*v1[1])/vec0[2]+v1[2];
749
750         zxd= -vec0[0]/vec0[2];
751         zyd= -vec0[1]/vec0[2];
752         zy0= my2*zyd+xx1;
753         zd= (int)CLAMPIS(zxd, INT_MIN, INT_MAX);
754
755         /* start-ofset in rect */
756         /*      rectx= R.rectx;  */
757         /* I suspect this var needs very careful setting... When edge rendering  */
758         /* is on, this is strange */
759         rectx   = imWidth;
760         yoffset = my2;
761         xoffset = 0;
762         
763         xs3= 0;         /* flag */
764         if(dx0>dx1) {
765                 MTC_swapInt(&xs0, &xs1);
766                 MTC_swapInt(&dx0, &dx1);
767                 xs3= 1; /* flag */
768
769         }
770
771         liney = yoffset;
772         for(y=my2;y>omsl;y--) {
773
774                 sn1= xs0>>16;
775                 xs0+= dx0;
776
777                 sn2= xs1>>16;
778                 xs1+= dx1;
779
780                 sn1++;
781
782                 if(sn2>=rectx) sn2= rectx-1;
783                 if(sn1<0) sn1= 0;
784                 zverg= (int) CLAMPIS((sn1*zxd+zy0), INT_MIN, INT_MAX);
785
786                 linex = xoffset + sn1;
787                 liney = yoffset;
788                 
789                 x= sn2-sn1;
790                 
791                 while(x>=0) {
792                         insertInEdgeBuffer(linex , liney, zverg); /* line y not needed here */
793                         zverg+= zd;
794                         linex++;
795                         x--;
796                 }
797                 zy0-= zyd;
798                 yoffset--;
799         }
800
801         if(xs3) {
802                 xs0= xs1;
803                 dx0= dx1;
804         }
805         if(xs0>xs2) {
806                 xs3= xs0;
807                 xs0= xs2;
808                 xs2= xs3;
809                 xs3= dx0;
810                 dx0= dx2;
811                 dx2= xs3;
812         }
813
814         for(; y>=my0; y--) {
815
816                 sn1= xs0>>16;
817                 xs0+= dx0;
818
819                 sn2= xs2>>16;
820                 xs2+= dx2;
821
822                 sn1++;
823
824                 if(sn2>=rectx) sn2= rectx-1;
825                 if(sn1<0) sn1= 0;
826                 zverg= (int) CLAMPIS((sn1*zxd+zy0), INT_MIN, INT_MAX);
827                 
828                 linex = sn1;
829                 liney = yoffset;
830                                 
831                 x= sn2-sn1;
832       
833                 while(x>=0) {
834                         insertInEdgeBuffer(linex, liney, zverg); /* line y not needed here */
835                         zverg+= zd;
836                         linex++;
837                         x--;
838                 }
839                 zy0-=zyd;
840                 yoffset--;
841         }
842 } /* end of void fillEdgeRenderFace(float *v1, float *v2, float *v3) */
843
844 /* ------------------------------------------------------------------------- */
845
846 static void fillEdgeRenderEdge(unsigned int zvlnr, float *vec1, float *vec2)
847 {
848         int start, end, x, y, oldx, oldy, ofs;
849         int dz, vergz/*  , mask */;
850         float dx, dy;
851         float v1[3], v2[3];
852         int linex, liney;
853         int xoffset, yoffset;
854
855         
856         dx= vec2[0]-vec1[0];
857         dy= vec2[1]-vec1[1];
858         
859         if(fabs(dx) > fabs(dy)) {
860
861                 /* alle lines from left to right */
862                 if(vec1[0]<vec2[0]) {
863                         VECCOPY(v1, vec1);
864                         VECCOPY(v2, vec2);
865                 }
866                 else {
867                         VECCOPY(v2, vec1);
868                         VECCOPY(v1, vec2);
869                         dx= -dx; dy= -dy;
870                 }
871
872                 start= floor(v1[0]);
873                 end= start+floor(dx);
874                 if(end >= imWidth) end = imWidth - 1;
875                 
876                 oldy= floor(v1[1]);
877                 dy/= dx;
878                 
879                 vergz= v1[2];
880                 dz= (v2[2]-v1[2])/dx;
881                 
882                 yoffset = oldy;
883                 xoffset = start;
884                 
885                 if(dy<0) ofs= -1;
886                 else ofs= 1;
887
888                 liney = yoffset;
889                 linex = xoffset;
890                 
891                 for(x= start; x<=end; x++, linex++) {
892                         
893                         y= floor(v1[1]);
894                         if(y!=oldy) {
895                                 oldy= y;
896                                 liney+= ofs;
897                         }
898                         
899                         if(x>=0 && y>=Aminy && y<=Amaxy) {
900                                 insertInEdgeBuffer(linex , liney, vergz);
901                         }
902                         
903                         v1[1]+= dy;
904                         vergz+= dz;
905                 }
906         }
907         else {
908         
909                 /* all lines from top to bottom */
910                 if(vec1[1]<vec2[1]) {
911                         VECCOPY(v1, vec1);
912                         VECCOPY(v2, vec2);
913                 }
914                 else {
915                         VECCOPY(v2, vec1);
916                         VECCOPY(v1, vec2);
917                         dx= -dx; dy= -dy;
918                 }
919
920                 start= floor(v1[1]);
921                 end= start+floor(dy);
922                 
923                 if(start>Amaxy || end<Aminy) return;
924                 
925                 if(end>Amaxy) end= Amaxy;
926                 
927                 oldx= floor(v1[0]);
928                 dx/= dy;
929                 
930                 vergz= v1[2];
931                 dz= (v2[2]-v1[2])/dy;
932
933                 yoffset = start;
934                 xoffset = oldx;
935                                 
936                 if(dx<0) ofs= -1;
937                 else ofs= 1;
938
939                 linex = xoffset;
940                 liney = yoffset;
941                 
942                 for(y= start; y<=end; y++, liney++) {
943                         
944                         x= floor(v1[0]);
945                         if(x!=oldx) {
946                                 oldx= x;
947                                 linex += ofs;
948                         }
949                         
950                         if(x>=0 && y>=Aminy && (x < imWidth)) {
951                                 insertInEdgeBuffer(linex, liney, vergz);
952                         }
953                         
954                         v1[0]+= dx;
955                         vergz+= dz;
956                 }
957         }
958 } /* End of void fillEdgeRenderEdge(float *vec1, float *vec2) */
959
960 /* ------------------------------------------------------------------------- */
961
962 /* eof edgeRender.c */