A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
954 lines
25 KiB
C
954 lines
25 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* Contributors: 2004/2005/2006 Blender Foundation, full recode
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/* Add enhanced edges on a rendered image (toon shading, edge shading).
|
|
*/
|
|
|
|
/*
|
|
* Edge rendering: use a mask to weigh the depth of neighbouring
|
|
* pixels, and do a colour correction.
|
|
*
|
|
* We need:
|
|
* - a buffer to store the depths (ints)
|
|
* - a function that alters the colours in R.rectot (copy edge_enhance?)
|
|
* The max. z buffer depth is 0x7FFF.FFFF (7 F's)
|
|
*
|
|
* - We 'ignore' the pixels falling outside the regular buffer (we fill)
|
|
* these with the max depth. This causes artefacts when rendering in
|
|
* parts.
|
|
*/
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* enable extra bounds checking and tracing */
|
|
/* #define RE_EDGERENDERSAFE */
|
|
/* disable the actual edge correction */
|
|
/* #define RE_EDGERENDER_NO_CORRECTION */
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <limits.h> /* INT_MIN,MAX are used here */
|
|
#include <stdio.h>
|
|
|
|
#include "DNA_material_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
#include "MTC_vectorops.h"
|
|
#include "BKE_utildefines.h"
|
|
#include "BLI_jitter.h"
|
|
|
|
#include "render_types.h"
|
|
#include "renderpipeline.h"
|
|
#include "edgeRender.h"
|
|
#include "zbuf.h" /* for zbufclipwire and zbufclip */
|
|
|
|
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
|
|
/* only to be used here in this file, it's for speed */
|
|
extern struct Render R;
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
|
|
/* exp: */
|
|
static Material** matBuffer; /* buffer with material indices */
|
|
static Material* mat_cache; /* material of the face being buffered */
|
|
|
|
static char* colBuffer; /* buffer with colour correction */
|
|
static int *edgeBuffer; /* buffer with distances */
|
|
static int bufWidth; /* x-dimension of the buffer */
|
|
static int bufHeight; /* y-dimension of the buffer */
|
|
static int imWidth; /* x-dimension of the image */
|
|
static int imHeight; /* y-dimension of the image */
|
|
static int osaCount; /* oversample count */
|
|
static int maskBorder; /* size of the mask border */
|
|
static short int intensity; /* edge intensity */
|
|
static short int same_mat_redux; /* intensity reduction for boundaries with the same material */
|
|
static int compatible_mode; /* edge positioning compatible with old rederer */
|
|
static int selectmode; /* 0: only solid faces, 1: also transparent f's */
|
|
|
|
static int Aminy; /* y value of first line in the accu buffer */
|
|
static int Amaxy; /* y value of last line in the accu buffer */
|
|
/* -also used to clip when zbuffering */
|
|
static char edgeR; /* Colour for the edges. The edges will receive */
|
|
static char edgeG; /* this tint. The colour is fully used! */
|
|
static char edgeB;
|
|
/* static float edgeBlend; */ /* Indicate opaqueness of the edge colour. */
|
|
|
|
/* Local functions --------------------------------------------------------- */
|
|
/**
|
|
* Initialise the edge render buffer memory.
|
|
*/
|
|
static void initEdgeRenderBuffer(void);
|
|
/**
|
|
* Release buffer memory.
|
|
*/
|
|
static void freeEdgeRenderBuffer(void);
|
|
|
|
/**
|
|
* Set all distances in the distance buffer to the maximum distance.
|
|
*/
|
|
static void resetDistanceBuffer(void);
|
|
|
|
/**
|
|
* Insert this distance at these pixel coordinates.
|
|
*/
|
|
static void insertInEdgeBuffer(int x, int y, int dist);
|
|
|
|
/**
|
|
* Renders enhanced edges. Distances from distRect are used to
|
|
* determine a correction on colourRect
|
|
*/
|
|
static void renderEdges(char * colourRect);
|
|
|
|
/**
|
|
* Buffer an edge between these two vertices in the e.r. distance buffer.
|
|
*/
|
|
static void fillEdgeRenderEdge(ZSpan *zspan, int, float *vec1, float *vec2);
|
|
|
|
/**
|
|
* Buffer a face between these two vertices in the e.r. distance buffer.
|
|
*/
|
|
static void fillEdgeRenderFace(struct ZSpan *zspan, int, float *v1, float *v2, float *v3, float *v4);
|
|
|
|
/**
|
|
* Compose the edge render colour buffer.
|
|
*/
|
|
static void calcEdgeRenderColBuf(char * tarbuf);
|
|
|
|
/**
|
|
* Loop over all objects that need to be edge rendered. This loop determines
|
|
* which objects get to be elected for edge rendering.
|
|
*/
|
|
static int zBufferEdgeRenderObjects(void);
|
|
|
|
/**
|
|
* Add edge pixels to the original image. It blends <bron> over <doel>.
|
|
*/
|
|
static void addEdgeOver(unsigned char *dst, unsigned char *src);
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* this is main call! */
|
|
void addEdges(char * targetbuf, int iw, int ih, int osanr,
|
|
short int intens, short int intens_redux, int compat, int mode, float r, float g, float b)
|
|
{
|
|
float rf, gf ,bf;
|
|
/* render parameters */
|
|
selectmode = mode;
|
|
imWidth = iw;
|
|
imHeight = ih;
|
|
compatible_mode = compat;
|
|
osaCount = osanr;
|
|
intensity = intens;
|
|
|
|
printf("Unsuported code!\n");
|
|
return;
|
|
|
|
/* Reduction doesn't exceed intensity. */
|
|
same_mat_redux = ((intens_redux < intensity)? intens_redux : intensity);
|
|
|
|
rf = r * 255.0;
|
|
if (rf > 255) edgeR = 255; else edgeR = rf;
|
|
gf = g * 255.0;
|
|
if (gf > 255) edgeG = 255; else edgeG = gf;
|
|
bf = b * 255.0;
|
|
if (bf > 255) edgeB = 255; else edgeB = bf;
|
|
|
|
/* Go! */
|
|
initEdgeRenderBuffer();
|
|
calcEdgeRenderColBuf(targetbuf);
|
|
freeEdgeRenderBuffer();
|
|
|
|
} /* end of void addEdges(char *, int, int, int, short int , int) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void initEdgeRenderBuffer()
|
|
{
|
|
char *ptr;
|
|
int i;
|
|
|
|
maskBorder = 1; /* for 3 by 3 mask*/
|
|
|
|
bufWidth = imWidth + (2 * maskBorder);
|
|
bufHeight = imHeight + (2 * maskBorder);
|
|
|
|
/* Experimental: store the material indices. */
|
|
if (same_mat_redux) {
|
|
matBuffer = MEM_callocN(sizeof(Material*)
|
|
* bufWidth * bufHeight, "matBuffer");
|
|
}
|
|
|
|
edgeBuffer = MEM_callocN(sizeof(int) * bufWidth * bufHeight, "edgeBuffer");
|
|
colBuffer = MEM_callocN(sizeof(char) * 4 * imWidth * imHeight, "colBuffer");
|
|
|
|
|
|
if ((edgeR != 0) || (edgeG != 0) || (edgeB != 0)) {
|
|
/* Set all colbuf pixels to the edge colour. Leave alpha channel */
|
|
/* cleared. Actually, we could blend in any image here... */
|
|
ptr = colBuffer;
|
|
for (i = 0; i < imWidth * imHeight; i++, ptr+=4)
|
|
{
|
|
ptr[0] = edgeR;
|
|
ptr[1] = edgeG;
|
|
ptr[2] = edgeB;
|
|
ptr[3] = 0;
|
|
}
|
|
}
|
|
|
|
} /* end of void initEdgeRenderBuffer(void) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
static void freeEdgeRenderBuffer(void)
|
|
{
|
|
if(edgeBuffer) MEM_freeN(edgeBuffer);
|
|
edgeBuffer= NULL;
|
|
if(colBuffer) MEM_freeN(colBuffer);
|
|
colBuffer= NULL;
|
|
if(matBuffer) MEM_freeN(matBuffer);
|
|
matBuffer= NULL;
|
|
} /* end of void freeEdgeRenderBuffer(void) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void resetDistanceBuffer(void)
|
|
{
|
|
int i;
|
|
for(i = 0; i < bufWidth * bufHeight; i++) edgeBuffer[i] = 0x7FFFFFFF;
|
|
} /* end of void resetDistanceBuffer(void) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void insertInEdgeBuffer(int x, int y, int dist)
|
|
{
|
|
int index;
|
|
|
|
/* +1? */
|
|
index = (y * bufWidth) + x + maskBorder;
|
|
|
|
/*exp: just dump a single index here. Maybe we can do more
|
|
* sophisticated things later on. */
|
|
if (same_mat_redux) {
|
|
matBuffer[index] = mat_cache;
|
|
}
|
|
|
|
if (edgeBuffer[index] >dist ) edgeBuffer[index] = dist;
|
|
|
|
} /* end of void insertInEdgeBuffer(int x, int y, int dist) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Modelled after rendercore.c/edge_enhance() */
|
|
static void renderEdges(char *colourRect)
|
|
{
|
|
/* use zbuffer to define edges, add it to the image */
|
|
int val, y, x, col, *rz, *rz1, *rz2, *rz3;
|
|
int targetoffset, heightoffset;
|
|
int zval1, zval2, zval3;
|
|
int i;
|
|
int matdif; /* For now: just a bogus int, 0 when all materials
|
|
* under the mask are the same, non-0 otherwise*/
|
|
int *matptr_low = 0, *matptr_cent = 0, *matptr_high = 0;
|
|
int matdiffac = 0;
|
|
char *cp;
|
|
|
|
#ifdef RE_EDGERENDER_NO_CORRECTION
|
|
return; /* no edge correction */
|
|
#endif
|
|
|
|
#ifdef RE_EDGERENDERSAFE
|
|
fprintf(stderr, "\n*** Activated full error trace on "
|
|
"edge rendering using:\n\t%s\n\t%s"
|
|
"\n*** Rendering edges at %d intensity",
|
|
edgeRender_c, edgeRender_h, intensity);
|
|
#endif
|
|
|
|
|
|
/* Old renderer uses wrong positions! With the compat switch on, the po- */
|
|
/* sitions will be corrected to be offset in the same way. */
|
|
if (compatible_mode) {
|
|
targetoffset = 4 * (imWidth - 1);
|
|
heightoffset = -1;
|
|
} else {
|
|
targetoffset = 0;
|
|
heightoffset = 0;
|
|
}
|
|
|
|
/* Fill edges with some default values. We just copy what is in the edge */
|
|
/* This looks messy, but it appears to be ok. */
|
|
edgeBuffer[0] = edgeBuffer[bufWidth + 1];
|
|
edgeBuffer[bufWidth - 1] = edgeBuffer[(2 * bufWidth) - 2];
|
|
edgeBuffer[bufWidth * (bufHeight - 1)] =
|
|
edgeBuffer[bufWidth * (bufHeight - 2) + 1];
|
|
edgeBuffer[(bufWidth * bufHeight) - 1] =
|
|
edgeBuffer[(bufWidth * (bufHeight - 1)) - 2];
|
|
for (i = 1; i < bufWidth - 1; i++) { /* lieing edges */
|
|
edgeBuffer[i] = edgeBuffer[bufWidth + i]; /* bottom*/
|
|
edgeBuffer[((bufHeight - 1)*bufWidth) + i]
|
|
= edgeBuffer[((bufHeight - 2)*bufWidth) + i]; /* top */
|
|
}
|
|
for (i = 1; i < bufHeight - 2; i++) { /* standing edges */
|
|
edgeBuffer[i * bufWidth] = edgeBuffer[(i * bufWidth) + 1]; /* left */
|
|
edgeBuffer[((i + 1) * bufWidth) - 1] =
|
|
edgeBuffer[((i + 1) * bufWidth) - 2]; /* right */
|
|
}
|
|
|
|
/* same hack for the materials: */
|
|
if (same_mat_redux) {
|
|
matBuffer[0] = matBuffer[bufWidth + 1];
|
|
matBuffer[bufWidth - 1] = matBuffer[(2 * bufWidth) - 2];
|
|
matBuffer[bufWidth * (bufHeight - 1)] =
|
|
matBuffer[bufWidth * (bufHeight - 2) + 1];
|
|
matBuffer[(bufWidth * bufHeight) - 1] =
|
|
matBuffer[(bufWidth * (bufHeight - 1)) - 2];
|
|
for (i = 1; i < bufWidth - 1; i++) { /* lieing mats */
|
|
matBuffer[i] = matBuffer[bufWidth + i]; /* bottom*/
|
|
matBuffer[((bufHeight - 1)*bufWidth) + i]
|
|
= matBuffer[((bufHeight - 2)*bufWidth) + i]; /* top */
|
|
}
|
|
for (i = 1; i < bufHeight - 2; i++) { /* standing mats */
|
|
matBuffer[i * bufWidth] = matBuffer[(i * bufWidth) + 1]; /* left */
|
|
matBuffer[((i + 1) * bufWidth) - 1] =
|
|
matBuffer[((i + 1) * bufWidth) - 2]; /* right */
|
|
}
|
|
}
|
|
|
|
/* shift values in zbuffer 4 to the right, for filter we need multiplying with 12 max */
|
|
rz = edgeBuffer;
|
|
if(rz==0) return;
|
|
|
|
for(y=0; y < bufHeight * bufWidth; y++, rz++) {
|
|
(*rz)>>= 4;
|
|
}
|
|
|
|
/* Distance pointers */
|
|
rz1= edgeBuffer;
|
|
rz2= rz1 + bufWidth;
|
|
rz3= rz2 + bufWidth;
|
|
|
|
if (same_mat_redux) {
|
|
matptr_low = (int *) matBuffer;
|
|
matptr_cent = matptr_low + bufWidth;
|
|
matptr_high = matptr_cent + bufWidth;
|
|
}
|
|
|
|
if (osaCount == 1) {
|
|
cp = colourRect + targetoffset;
|
|
} else {
|
|
cp = colBuffer + targetoffset;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
for(y = 0; y < (imHeight + heightoffset) ; y++) {
|
|
|
|
|
|
/* All these indices are a bit silly. I need to
|
|
* rewrite this, so all buffers use the same
|
|
* indexing. */
|
|
for(x = 0;
|
|
x < imWidth;
|
|
x++, rz1++, rz2++, rz3++, cp+=4,
|
|
matptr_low++,
|
|
matptr_cent++,
|
|
matptr_high++) {
|
|
|
|
/* prevent overflow with sky z values */
|
|
zval1= rz1[0] + 2*rz1[1] + rz1[2];
|
|
zval2= 2*rz2[0] + 2*rz2[2];
|
|
zval3= rz3[0] + 2*rz3[1] + rz3[2];
|
|
|
|
col= abs ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 );
|
|
|
|
/* Several options for matdif:
|
|
*
|
|
* - suppress all boundaries with 0 dif
|
|
*
|
|
* - weaken col dif? Or decrease intensity by
|
|
* a factor when non 0 dif??
|
|
*/
|
|
|
|
/* exp: matdif is non-0 if the mask-center
|
|
* material differs from any of the
|
|
* corners. */
|
|
|
|
if (same_mat_redux) {
|
|
matdif = abs (matptr_cent[1] - matptr_low[0])
|
|
+ abs (matptr_cent[1] - matptr_low[1])
|
|
+ abs (matptr_cent[1] - matptr_low[2])
|
|
+ abs (matptr_cent[1] - matptr_cent[0])
|
|
+ abs (matptr_cent[1] - matptr_low[2])
|
|
+ abs (matptr_cent[1] - matptr_high[0])
|
|
+ abs (matptr_cent[1] - matptr_high[1])
|
|
+ abs (matptr_cent[1] - matptr_high[2]);
|
|
|
|
matdiffac = (matdif ? 0 : same_mat_redux);
|
|
}
|
|
col >>= 5;
|
|
if(col > (1<<16)) col= (1<<16);
|
|
col= ((intensity - matdiffac) * col)>>8;
|
|
if(col>255) col= 255;
|
|
|
|
/* Colour edge if
|
|
*
|
|
* 1. there is an appreciable, non-uniform
|
|
* gradient,
|
|
*
|
|
* 2. there are different materials bordering
|
|
* on the center pixel
|
|
*/
|
|
if( (col>0)
|
|
/* && (matdif != 0) */) {
|
|
|
|
if(osaCount > 1) {
|
|
/* Currently done by tweaking alpha. The colBuffer is */
|
|
/* filled with pixels of the colour appropriate for the */
|
|
/* edges. This colour is alpha-blended over the image. */
|
|
/* This calculation determines how much colour each pixel */
|
|
/* gets. */
|
|
col/= osaCount;
|
|
val= cp[3]+col;
|
|
if(val>255) cp[3]= 255; else cp[3]= val;
|
|
}
|
|
else {
|
|
/* the pixel is blackened when col is too big */
|
|
val = cp[0] - col;
|
|
if(val<=0) {
|
|
cp[0]= edgeR;
|
|
} else {
|
|
cp[0]= val;
|
|
}
|
|
val = cp[1] - col;
|
|
if(val<=0) {
|
|
cp[1]= edgeG;
|
|
}else {
|
|
cp[1]= val;
|
|
}
|
|
val = cp[2] - col;
|
|
if(val<=0) {
|
|
cp[2]= edgeB;
|
|
} else {
|
|
cp[2]= val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rz1+= 2;
|
|
rz2+= 2;
|
|
rz3+= 2;
|
|
if (same_mat_redux) {
|
|
matptr_low += 2;
|
|
matptr_cent += 2;
|
|
matptr_high += 2;
|
|
}
|
|
|
|
}
|
|
|
|
} /* end of void renderEdges() */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* adds src to dst */
|
|
static void addEdgeOver(unsigned char *dst, unsigned char *src)
|
|
{
|
|
unsigned char inverse;
|
|
unsigned char alpha;
|
|
unsigned int c;
|
|
|
|
alpha = src[3];
|
|
|
|
if( alpha == 0) return;
|
|
if( alpha == 255) {
|
|
/* when full opacity, just copy the pixel */
|
|
/* this code assumes an int is 32 bit, fix
|
|
|
|
*(( unsigned int *)dst)= *((unsigned int *)src);
|
|
replaced with memcpy
|
|
*/
|
|
memcpy(dst,src,4);
|
|
return;
|
|
}
|
|
|
|
/* This must be a special blend-mode, because we get a 'weird' data */
|
|
/* input format now. With edge = (c_e, a_e), picture = (c_p, a_p), we */
|
|
/* get: result = ( c_e*a_e + c_p(1 - a_e), a_p ). */
|
|
|
|
inverse = 255 - alpha;
|
|
|
|
c = ((unsigned int)inverse * (unsigned int) dst[0] + (unsigned int)src[0] *
|
|
(unsigned int)alpha) >> 8;
|
|
dst[0] = c;
|
|
|
|
c = ((unsigned int)inverse * (unsigned int) dst[1] + (unsigned int)src[1] *
|
|
(unsigned int)alpha) >> 8;
|
|
dst[1] = c;
|
|
c = ((unsigned int)inverse * (unsigned int) dst[2] + (unsigned int)src[2] *
|
|
(unsigned int)alpha) >> 8;
|
|
dst[2] = c;
|
|
}
|
|
|
|
static void calcEdgeRenderColBuf(char* colTargetBuffer)
|
|
{
|
|
int keepLooping = 1;
|
|
int sample;
|
|
|
|
/* zbuffer fix: here? */
|
|
// Zmulx= ((float) imWidth)/2.0;
|
|
// Zmuly= ((float) imHeight)/2.0;
|
|
|
|
/* always buffer the max. extent */
|
|
Aminy = 0;
|
|
Amaxy = imHeight;
|
|
|
|
sample = 0; /* Zsample is used internally ! */
|
|
while ( (sample < osaCount) && keepLooping ) {
|
|
/* jitter */
|
|
// Zjitx= -R.jit[sample][0];
|
|
// Zjity= -R.jit[sample][1];
|
|
|
|
/* should reset dis buffer here */
|
|
resetDistanceBuffer();
|
|
|
|
/* kick all into a z buffer */
|
|
keepLooping = zBufferEdgeRenderObjects();
|
|
|
|
/* do filtering */
|
|
renderEdges(colTargetBuffer);
|
|
|
|
if(R.test_break()) keepLooping = 0;
|
|
sample++;
|
|
}
|
|
|
|
/* correction for osa-sampling...*/
|
|
if( osaCount != 1) {
|
|
unsigned char *rp, *rt;
|
|
int a;
|
|
|
|
rt= colTargetBuffer;
|
|
rp= colBuffer;
|
|
for(a = imWidth * imHeight; a>0; a--, rt+=4, rp+=4) {
|
|
/* there seem to be rounding errors here... */
|
|
addEdgeOver(rt, rp);
|
|
}
|
|
}
|
|
|
|
} /*End of void calcEdgeRenderZBuf(void) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Clip flags etc. should still be set. When called in the span of 'normal' */
|
|
/* rendering, this should be ok. */
|
|
static int zBufferEdgeRenderObjects(void)
|
|
{
|
|
ZSpan zspan;
|
|
VlakRen *vlr= NULL;
|
|
Material *ma;
|
|
unsigned int zvlnr;
|
|
int keepLooping;
|
|
int faceCounter; /* counter for face number */
|
|
|
|
zbuf_alloc_span(&zspan, imWidth, imHeight);
|
|
|
|
/* needed for transform from hoco to zbuffer co */
|
|
zspan.zmulx= ((float)imWidth)/2.0;
|
|
zspan.zmuly= ((float)imHeight)/2.0;
|
|
zspan.zofsx= -0.5f;
|
|
zspan.zofsy= -0.5f;
|
|
|
|
/* the buffers ??? */
|
|
|
|
/* filling methods */
|
|
zspan.zbuffunc = fillEdgeRenderFace;
|
|
zspan.zbuflinefunc = fillEdgeRenderEdge;
|
|
|
|
keepLooping = 1;
|
|
ma = NULL;
|
|
faceCounter = 0;
|
|
|
|
while ( (faceCounter < R.totvlak) && keepLooping) {
|
|
if((faceCounter & 255)==0) { vlr= R.blovl[faceCounter>>8]; }
|
|
else vlr++;
|
|
|
|
ma= vlr->mat;
|
|
|
|
/*exp*/
|
|
mat_cache = ma;
|
|
|
|
/* face number is used in the fill functions */
|
|
zvlnr = faceCounter + 1;
|
|
|
|
if(vlr->flag & R_VISIBLE) {
|
|
|
|
/* here we cull all transparent faces if mode == 0 */
|
|
if (selectmode || !(ma->mode & MA_ZTRA)) {
|
|
/* here we can add all kinds of extra selection criteria */
|
|
if(ma->mode & (MA_WIRE)) zbufclipwire(&zspan, zvlnr, vlr);
|
|
else {
|
|
zbufclip(&zspan, zvlnr, vlr->v1->ho, vlr->v2->ho, vlr->v3->ho,
|
|
vlr->v1->clip, vlr->v2->clip, vlr->v3->clip);
|
|
if(vlr->v4) {
|
|
zvlnr+= 0x800000; /* in a sense, the 'adjoint' face */
|
|
zbufclip(&zspan, zvlnr, vlr->v1->ho, vlr->v3->ho, vlr->v4->ho,
|
|
vlr->v1->clip, vlr->v3->clip, vlr->v4->clip);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
if(R.test_break()) keepLooping = 0;
|
|
faceCounter++;
|
|
}
|
|
return keepLooping;
|
|
} /* End of int zBufferEdgeRenderObjects(void) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void fillEdgeRenderFace(struct ZSpan *zspan, int zvlnr, float *v1, float *v2, float *v3, float *v4)
|
|
{
|
|
/* Coordinates of the vertices are specified in ZCS */
|
|
double z0; /* used as temp var*/
|
|
double xx1;
|
|
double zxd,zyd,zy0, tmp;
|
|
float *minv,*maxv,*midv;
|
|
int zverg,x;
|
|
int my0,my2,sn1,sn2,rectx,zd;
|
|
int y,omsl,xs0,xs1,xs2,xs3, dx0,dx1,dx2/* , mask */;
|
|
int linex, liney, xoffset, yoffset; /* pointers to the pixel number */
|
|
|
|
/* These used to be doubles. We may want to change them back if the */
|
|
/* loss of accuracy proves to be a problem? There does not seem to be */
|
|
/* any performance issues here, so I'll just keep the doubles. */
|
|
/* float vec0[3], vec1[3], vec2[3]; */
|
|
double vec0[3], vec1[3], vec2[3];
|
|
|
|
/* MIN MAX */
|
|
/* sort vertices for min mid max y value */
|
|
if(v1[1]<v2[1]) {
|
|
if(v2[1]<v3[1]) { minv=v1; midv=v2; maxv=v3;}
|
|
else if(v1[1]<v3[1]) { minv=v1; midv=v3; maxv=v2;}
|
|
else { minv=v3; midv=v1; maxv=v2;}
|
|
}
|
|
else {
|
|
if(v1[1]<v3[1]) { minv=v2; midv=v1; maxv=v3;}
|
|
else if(v2[1]<v3[1]) { minv=v2; midv=v3; maxv=v1;}
|
|
else { minv=v3; midv=v2; maxv=v1;}
|
|
}
|
|
|
|
if(minv[1] == maxv[1]) return; /* security, for 'zero' size faces */
|
|
|
|
my0 = ceil(minv[1]);
|
|
my2 = floor(maxv[1]);
|
|
omsl = floor(midv[1]);
|
|
|
|
/* outside the current z buffer slice: clip whole face */
|
|
if( (my2 < Aminy) || (my0 > Amaxy)) return;
|
|
|
|
if(my0<Aminy) my0= Aminy;
|
|
|
|
/* EDGES : THE LONGEST */
|
|
xx1= maxv[1]-minv[1];
|
|
if(xx1>2.0/65536.0) {
|
|
z0= (maxv[0]-minv[0])/xx1;
|
|
|
|
tmp= (-65536.0*z0);
|
|
dx0= CLAMPIS(tmp, INT_MIN, INT_MAX);
|
|
|
|
tmp= 65536.0*(z0*(my2-minv[1])+minv[0]);
|
|
xs0= CLAMPIS(tmp, INT_MIN, INT_MAX);
|
|
}
|
|
else {
|
|
dx0= 0;
|
|
xs0= 65536.0*(MIN2(minv[0],maxv[0]));
|
|
}
|
|
/* EDGES : THE TOP ONE */
|
|
xx1= maxv[1]-midv[1];
|
|
if(xx1>2.0/65536.0) {
|
|
z0= (maxv[0]-midv[0])/xx1;
|
|
|
|
tmp= (-65536.0*z0);
|
|
dx1= CLAMPIS(tmp, INT_MIN, INT_MAX);
|
|
|
|
tmp= 65536.0*(z0*(my2-midv[1])+midv[0]);
|
|
xs1= CLAMPIS(tmp, INT_MIN, INT_MAX);
|
|
}
|
|
else {
|
|
dx1= 0;
|
|
xs1= 65536.0*(MIN2(midv[0],maxv[0]));
|
|
}
|
|
/* EDGES : THE BOTTOM ONE */
|
|
xx1= midv[1]-minv[1];
|
|
if(xx1>2.0/65536.0) {
|
|
z0= (midv[0]-minv[0])/xx1;
|
|
|
|
tmp= (-65536.0*z0);
|
|
dx2= CLAMPIS(tmp, INT_MIN, INT_MAX);
|
|
|
|
tmp= 65536.0*(z0*(omsl-minv[1])+minv[0]);
|
|
xs2= CLAMPIS(tmp, INT_MIN, INT_MAX);
|
|
}
|
|
else {
|
|
dx2= 0;
|
|
xs2= 65536.0*(MIN2(minv[0],midv[0]));
|
|
}
|
|
|
|
/* ZBUF DX DY */
|
|
MTC_diff3DFF(vec1, v1, v2);
|
|
MTC_diff3DFF(vec2, v2, v3);
|
|
MTC_cross3Double(vec0, vec1, vec2);
|
|
|
|
/* cross product of two of the sides is 0 => this face is too small */
|
|
if(vec0[2]==0.0) return;
|
|
|
|
if(midv[1] == maxv[1]) omsl= my2;
|
|
if(omsl < Aminy) omsl= Aminy-1; /* that way it does the first loop entirely */
|
|
|
|
while (my2 > Amaxy) { /* my2 can really be larger */
|
|
xs0+=dx0;
|
|
if (my2<=omsl) {
|
|
xs2+= dx2;
|
|
}
|
|
else{
|
|
xs1+= dx1;
|
|
}
|
|
my2--;
|
|
}
|
|
|
|
xx1= (vec0[0]*v1[0]+vec0[1]*v1[1])/vec0[2]+v1[2];
|
|
|
|
zxd= -vec0[0]/vec0[2];
|
|
zyd= -vec0[1]/vec0[2];
|
|
zy0= my2*zyd+xx1;
|
|
zd= (int)CLAMPIS(zxd, INT_MIN, INT_MAX);
|
|
|
|
/* start-ofset in rect */
|
|
/* rectx= R.rectx; */
|
|
/* I suspect this var needs very careful setting... When edge rendering */
|
|
/* is on, this is strange */
|
|
rectx = imWidth;
|
|
yoffset = my2;
|
|
xoffset = 0;
|
|
|
|
xs3= 0; /* flag */
|
|
if(dx0>dx1) {
|
|
MTC_swapInt(&xs0, &xs1);
|
|
MTC_swapInt(&dx0, &dx1);
|
|
xs3= 1; /* flag */
|
|
|
|
}
|
|
|
|
liney = yoffset;
|
|
for(y=my2;y>omsl;y--) {
|
|
|
|
sn1= xs0>>16;
|
|
xs0+= dx0;
|
|
|
|
sn2= xs1>>16;
|
|
xs1+= dx1;
|
|
|
|
sn1++;
|
|
|
|
if(sn2>=rectx) sn2= rectx-1;
|
|
if(sn1<0) sn1= 0;
|
|
zverg= (int) CLAMPIS((sn1*zxd+zy0), INT_MIN, INT_MAX);
|
|
|
|
linex = xoffset + sn1;
|
|
liney = yoffset;
|
|
|
|
x= sn2-sn1;
|
|
|
|
while(x>=0) {
|
|
insertInEdgeBuffer(linex , liney, zverg); /* line y not needed here */
|
|
zverg+= zd;
|
|
linex++;
|
|
x--;
|
|
}
|
|
zy0-= zyd;
|
|
yoffset--;
|
|
}
|
|
|
|
if(xs3) {
|
|
xs0= xs1;
|
|
dx0= dx1;
|
|
}
|
|
if(xs0>xs2) {
|
|
xs3= xs0;
|
|
xs0= xs2;
|
|
xs2= xs3;
|
|
xs3= dx0;
|
|
dx0= dx2;
|
|
dx2= xs3;
|
|
}
|
|
|
|
for(; y>=my0; y--) {
|
|
|
|
sn1= xs0>>16;
|
|
xs0+= dx0;
|
|
|
|
sn2= xs2>>16;
|
|
xs2+= dx2;
|
|
|
|
sn1++;
|
|
|
|
if(sn2>=rectx) sn2= rectx-1;
|
|
if(sn1<0) sn1= 0;
|
|
zverg= (int) CLAMPIS((sn1*zxd+zy0), INT_MIN, INT_MAX);
|
|
|
|
linex = sn1;
|
|
liney = yoffset;
|
|
|
|
x= sn2-sn1;
|
|
|
|
while(x>=0) {
|
|
insertInEdgeBuffer(linex, liney, zverg); /* line y not needed here */
|
|
zverg+= zd;
|
|
linex++;
|
|
x--;
|
|
}
|
|
zy0-=zyd;
|
|
yoffset--;
|
|
}
|
|
} /* end of void fillEdgeRenderFace(float *v1, float *v2, float *v3) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void fillEdgeRenderEdge(ZSpan *zspan, int zvlnr, float *vec1, float *vec2)
|
|
{
|
|
int start, end, x, y, oldx, oldy, ofs;
|
|
int dz, vergz/* , mask */;
|
|
float dx, dy;
|
|
float v1[3], v2[3];
|
|
int linex, liney;
|
|
int xoffset, yoffset;
|
|
|
|
|
|
dx= vec2[0]-vec1[0];
|
|
dy= vec2[1]-vec1[1];
|
|
|
|
if(fabs(dx) > fabs(dy)) {
|
|
|
|
/* alle lines from left to right */
|
|
if(vec1[0]<vec2[0]) {
|
|
VECCOPY(v1, vec1);
|
|
VECCOPY(v2, vec2);
|
|
}
|
|
else {
|
|
VECCOPY(v2, vec1);
|
|
VECCOPY(v1, vec2);
|
|
dx= -dx; dy= -dy;
|
|
}
|
|
|
|
start= floor(v1[0]);
|
|
end= start+floor(dx);
|
|
if(end >= imWidth) end = imWidth - 1;
|
|
|
|
oldy= floor(v1[1]);
|
|
dy/= dx;
|
|
|
|
vergz= v1[2];
|
|
dz= (v2[2]-v1[2])/dx;
|
|
|
|
yoffset = oldy;
|
|
xoffset = start;
|
|
|
|
if(dy<0) ofs= -1;
|
|
else ofs= 1;
|
|
|
|
liney = yoffset;
|
|
linex = xoffset;
|
|
|
|
for(x= start; x<=end; x++, linex++) {
|
|
|
|
y= floor(v1[1]);
|
|
if(y!=oldy) {
|
|
oldy= y;
|
|
liney+= ofs;
|
|
}
|
|
|
|
if(x>=0 && y>=Aminy && y<=Amaxy) {
|
|
insertInEdgeBuffer(linex , liney, vergz);
|
|
}
|
|
|
|
v1[1]+= dy;
|
|
vergz+= dz;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* all lines from top to bottom */
|
|
if(vec1[1]<vec2[1]) {
|
|
VECCOPY(v1, vec1);
|
|
VECCOPY(v2, vec2);
|
|
}
|
|
else {
|
|
VECCOPY(v2, vec1);
|
|
VECCOPY(v1, vec2);
|
|
dx= -dx; dy= -dy;
|
|
}
|
|
|
|
start= floor(v1[1]);
|
|
end= start+floor(dy);
|
|
|
|
if(start>Amaxy || end<Aminy) return;
|
|
|
|
if(end>Amaxy) end= Amaxy;
|
|
|
|
oldx= floor(v1[0]);
|
|
dx/= dy;
|
|
|
|
vergz= v1[2];
|
|
dz= (v2[2]-v1[2])/dy;
|
|
|
|
yoffset = start;
|
|
xoffset = oldx;
|
|
|
|
if(dx<0) ofs= -1;
|
|
else ofs= 1;
|
|
|
|
linex = xoffset;
|
|
liney = yoffset;
|
|
|
|
for(y= start; y<=end; y++, liney++) {
|
|
|
|
x= floor(v1[0]);
|
|
if(x!=oldx) {
|
|
oldx= x;
|
|
linex += ofs;
|
|
}
|
|
|
|
if(x>=0 && y>=Aminy && (x < imWidth)) {
|
|
insertInEdgeBuffer(linex, liney, vergz);
|
|
}
|
|
|
|
v1[0]+= dx;
|
|
vergz+= dz;
|
|
}
|
|
}
|
|
} /* End of void fillEdgeRenderEdge(float *vec1, float *vec2) */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* eof edgeRender.c */
|