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