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