Files
test/source/blender/render/intern/source/rendercore.c
Ton Roosendaal d024452ebf Orange branch: Revived hidden treasure, the Groups!
Previous experiment (in 2000) didn't satisfy, it had even some primitive
NLA option in groups... so, cleaned up the old code (removed most) and
integrated it back in a more useful way.

Usage:
- CTRL+G gives menu to add group, add to existing group, or remove from
  groups.
- In Object buttons, a new (should become first) Panel was added, showing
  not only Object "ID button" and Parent, but also the Groups the Object
  Belongs to. These buttons also allow rename, assigning or removing.
- To indicate Objects are grouped, they're drawn in a (not theme yet, so
  temporal?) green wire color.
- Use ALT+SHIFT mouse-select to (de)select an entire group

But, the real power of groups is in the following features:

-> Particle Force field and Guide control
In the "Particle Motion" Panel, you can indicate a Group name, this then
limits force fields or guides to members of that Group. (Note that layers
still work on top of that... not sure about that).

-> Light Groups
In the Material "Shaders" Panel, you can indicate a Group name to limit
lighting for the Material to lamps in this group. The Lights in a Group do
need to be 'visible' for the Scene to be rendered (as usual).

-> Group Duplicator
In the Object "Anim" Panel, you can set any Object (use Empty!) to
duplicate an entire Group. It will make copies of all Objects in that Group.
Also works for animated Objects, but it will copy the current positions or
deforms. Control over 'local timing' (so we can do Massive anims!) will be
added later.
(Note; this commit won't render Group duplicators yet, a fix in bf-blender
will enable that, next commit will sync)

-> Library Appending
In the SHIFT-F1 or SHIFT+F4 browsers, you can also find the Groups listed.
By appending or linking the Group itself, and use the Group Duplicator, you
now can animate and position linked Objects. The nice thing is that the
local saved file itself will only store the Group name that was linked, so
on a next file read, the Group Objects will be re-read as stored (changed)
in the Library file.
(Note; current implementation also "gives a base" to linked Group Objects,
to show them as Objects in the current Scene. Need that now for testing
purposes, but probably will be removed later).

-> Outliner
Outliner now shows Groups as optio too, nice to organize your data a bit too!

In General, Groups have a very good potential... for example, it could
become default for MetaBall Objects too (jiri, I can help you later on how
this works). All current 'layer relationships' in Blender should be dropped
in time, I guess...
2005-12-06 10:55:30 +00:00

3329 lines
80 KiB
C

/**
* $Id$
*
* ***** BEGIN GPL/BL DUAL 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. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* 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.
*
* The Original Code is: all of this file.
*
* Contributor(s): Hos, Robert Wenzlaff.
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
/* system includes */
#include <math.h>
#include <string.h>
#include <stdlib.h>
/* External modules: */
#include "MEM_guardedalloc.h"
#include "BLI_arithb.h"
#include "MTC_matrixops.h"
#include "BKE_utildefines.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_image_types.h"
#include "DNA_lamp_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_texture_types.h"
#include "BKE_global.h"
#include "BKE_texture.h"
#include "BLI_rand.h"
/* local include */
#include "RE_callbacks.h"
#include "render.h"
#include "zbuf.h" /* stuff like bgnaccumbuf, fillrect, ...*/
#include "pixelblending.h"
#include "pixelshading.h"
#include "vanillaRenderPipe.h" /* transfercolour... */
#include "gammaCorrectionTables.h"
#include "shadbuf.h"
#include "renderHelp.h"
#include "jitter.h"
#include "texture.h"
/* own include */
#include "rendercore.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "SDL_thread.h"
/* global for this file. struct render will be more dynamic later, to allow multiple renderers */
RE_Render R;
float bluroffsx=0.0, bluroffsy=0.0; // set in initrender.c (bad, ton)
/* x and y are current pixels to be rendered */
void calc_view_vector(float *view, float x, float y)
{
if(R.r.mode & R_ORTHO) {
view[0]= view[1]= 0.0;
}
else {
view[0]= (x+(R.xstart)+bluroffsx +0.5);
if(R.flag & R_SEC_FIELD) {
if(R.r.mode & R_ODDFIELD) view[1]= (y+R.ystart)*R.ycor;
else view[1]= (y+R.ystart+1.0)*R.ycor;
}
else view[1]= (y+R.ystart+bluroffsy+0.5)*R.ycor;
}
view[2]= -R.viewfac;
if(R.r.mode & R_PANORAMA) {
float panoco, panosi, u, v;
panoco = getPanovCo();
panosi = getPanovSi();
u= view[0]; v= view[2];
view[0]= panoco*u + panosi*v;
view[2]= -panosi*u + panoco*v;
}
}
#if 0
static void fogcolor(float *colf, float *rco, float *view)
{
float alpha, stepsize, startdist, dist, hor[4], zen[3], vec[3], dview[3];
float div=0.0f, distfac;
hor[0]= R.wrld.horr; hor[1]= R.wrld.horg; hor[2]= R.wrld.horb;
zen[0]= R.wrld.zenr; zen[1]= R.wrld.zeng; zen[2]= R.wrld.zenb;
VECCOPY(vec, rco);
/* we loop from cur coord to mist start in steps */
stepsize= 1.0f;
div= ABS(view[2]);
dview[0]= view[0]/(stepsize*div);
dview[1]= view[1]/(stepsize*div);
dview[2]= -stepsize;
startdist= -rco[2] + BLI_frand();
for(dist= startdist; dist>R.wrld.miststa; dist-= stepsize) {
hor[0]= R.wrld.horr; hor[1]= R.wrld.horg; hor[2]= R.wrld.horb;
alpha= 1.0f;
do_sky_tex(vec, vec, NULL, hor, zen, &alpha);
distfac= (dist-R.wrld.miststa)/R.wrld.mistdist;
hor[3]= hor[0]*distfac*distfac;
/* premul! */
alpha= hor[3];
hor[0]= hor[0]*alpha;
hor[1]= hor[1]*alpha;
hor[2]= hor[2]*alpha;
addAlphaOverFloat(colf, hor);
VECSUB(vec, vec, dview);
}
}
#endif
float mistfactor(float zcor, float *co) /* dist en height, return alpha */
{
float fac, hi;
fac= zcor - R.wrld.miststa; /* zcor is calculated per pixel */
/* fac= -co[2]-R.wrld.miststa; */
if(fac>0.0) {
if(fac< R.wrld.mistdist) {
fac= (fac/(R.wrld.mistdist));
if(R.wrld.mistype==0) fac*= fac;
else if(R.wrld.mistype==1);
else fac= sqrt(fac);
}
else fac= 1.0;
}
else fac= 0.0;
/* height switched off mist */
if(R.wrld.misthi!=0.0 && fac!=0.0) {
/* at height misthi the mist is completely gone */
hi= R.viewinv[0][2]*co[0]+R.viewinv[1][2]*co[1]+R.viewinv[2][2]*co[2]+R.viewinv[3][2];
if(hi>R.wrld.misthi) fac= 0.0;
else if(hi>0.0) {
hi= (R.wrld.misthi-hi)/R.wrld.misthi;
fac*= hi*hi;
}
}
return (1.0-fac)* (1.0-R.wrld.misi);
}
/* external for preview only */
void RE_sky_char(float *view, char *col)
{
float f, colf[3];
float dither_value;
dither_value = ( (BLI_frand()-0.5)*R.r.dither_intensity)/256.0;
shadeSkyPixelFloat(colf, view, view, NULL);
f= 255.0*(colf[0]+dither_value);
if(f<=0.0) col[0]= 0; else if(f>255.0) col[0]= 255;
else col[0]= (char)f;
f= 255.0*(colf[1]+dither_value);
if(f<=0.0) col[1]= 0; else if(f>255.0) col[1]= 255;
else col[1]= (char)f;
f= 255.0*(colf[2]+dither_value);
if(f<=0.0) col[2]= 0; else if(f>255.0) col[2]= 255;
else col[2]= (char)f;
col[3]= 1; /* to prevent wrong optimalisation alphaover of flares */
}
/* ************************************** */
static void spothalo(struct LampRen *lar, ShadeInput *shi, float *intens)
{
double a, b, c, disc, nray[3], npos[3];
float t0, t1 = 0.0, t2= 0.0, t3, haint;
float p1[3], p2[3], ladist, maxz = 0.0, maxy = 0.0;
int snijp, doclip=1, use_yco=0;
int ok1=0, ok2=0;
*intens= 0.0;
haint= lar->haint;
if(R.r.mode & R_ORTHO) {
/* camera pos (view vector) cannot be used... */
/* camera position (cox,coy,0) rotate around lamp */
p1[0]= shi->co[0]-lar->co[0];
p1[1]= shi->co[1]-lar->co[1];
p1[2]= -lar->co[2];
MTC_Mat3MulVecfl(lar->imat, p1);
VECCOPY(npos, p1); // npos is double!
}
else {
VECCOPY(npos, lar->sh_invcampos); /* in initlamp calculated */
}
/* rotate view */
VECCOPY(nray, shi->view);
MTC_Mat3MulVecd(lar->imat, nray);
if(R.wrld.mode & WO_MIST) {
/* patchy... */
haint *= mistfactor(-lar->co[2], lar->co);
if(haint==0.0) {
return;
}
}
/* rotate maxz */
if(shi->co[2]==0.0) doclip= 0; /* for when halo at sky */
else {
p1[0]= shi->co[0]-lar->co[0];
p1[1]= shi->co[1]-lar->co[1];
p1[2]= shi->co[2]-lar->co[2];
maxz= lar->imat[0][2]*p1[0]+lar->imat[1][2]*p1[1]+lar->imat[2][2]*p1[2];
maxz*= lar->sh_zfac;
maxy= lar->imat[0][1]*p1[0]+lar->imat[1][1]*p1[1]+lar->imat[2][1]*p1[2];
if( fabs(nray[2]) <0.000001 ) use_yco= 1;
}
/* scale z to make sure volume is normalized */
nray[2]*= lar->sh_zfac;
/* nray does not need normalization */
ladist= lar->sh_zfac*lar->dist;
/* solve */
a = nray[0] * nray[0] + nray[1] * nray[1] - nray[2]*nray[2];
b = nray[0] * npos[0] + nray[1] * npos[1] - nray[2]*npos[2];
c = npos[0] * npos[0] + npos[1] * npos[1] - npos[2]*npos[2];
snijp= 0;
if (fabs(a) < 0.00000001) {
/*
* Only one intersection point...
*/
return;
}
else {
disc = b*b - a*c;
if(disc==0.0) {
t1=t2= (-b)/ a;
snijp= 2;
}
else if (disc > 0.0) {
disc = sqrt(disc);
t1 = (-b + disc) / a;
t2 = (-b - disc) / a;
snijp= 2;
}
}
if(snijp==2) {
/* sort */
if(t1>t2) {
a= t1; t1= t2; t2= a;
}
/* z of intersection points with diabolo */
p1[2]= npos[2] + t1*nray[2];
p2[2]= npos[2] + t2*nray[2];
/* evaluate both points */
if(p1[2]<=0.0) ok1= 1;
if(p2[2]<=0.0 && t1!=t2) ok2= 1;
/* at least 1 point with negative z */
if(ok1==0 && ok2==0) return;
/* intersction point with -ladist, the bottom of the cone */
if(use_yco==0) {
t3= (-ladist-npos[2])/nray[2];
/* de we have to replace one of the intersection points? */
if(ok1) {
if(p1[2]<-ladist) t1= t3;
}
else {
ok1= 1;
t1= t3;
}
if(ok2) {
if(p2[2]<-ladist) t2= t3;
}
else {
ok2= 1;
t2= t3;
}
}
else if(ok1==0 || ok2==0) return;
/* at least 1 visible interesction point */
if(t1<0.0 && t2<0.0) return;
if(t1<0.0) t1= 0.0;
if(t2<0.0) t2= 0.0;
if(t1==t2) return;
/* sort again to be sure */
if(t1>t2) {
a= t1; t1= t2; t2= a;
}
/* calculate t0: is the maximum visible z (when halo is intersected by face) */
if(doclip) {
if(use_yco==0) t0= (maxz-npos[2])/nray[2];
else t0= (maxy-npos[1])/nray[1];
if(t0<t1) return;
if(t0<t2) t2= t0;
}
/* calc points */
p1[0]= npos[0] + t1*nray[0];
p1[1]= npos[1] + t1*nray[1];
p1[2]= npos[2] + t1*nray[2];
p2[0]= npos[0] + t2*nray[0];
p2[1]= npos[1] + t2*nray[1];
p2[2]= npos[2] + t2*nray[2];
/* now we have 2 points, make three lengths with it */
a= sqrt(p1[0]*p1[0]+p1[1]*p1[1]+p1[2]*p1[2]);
b= sqrt(p2[0]*p2[0]+p2[1]*p2[1]+p2[2]*p2[2]);
c= VecLenf(p1, p2);
a/= ladist;
a= sqrt(a);
b/= ladist;
b= sqrt(b);
c/= ladist;
*intens= c*( (1.0-a)+(1.0-b) );
/* WATCH IT: do not clip a,b en c at 1.0, this gives nasty little overflows
at the edges (especially with narrow halos) */
if(*intens<=0.0) return;
/* soft area */
/* not needed because t0 has been used for p1/p2 as well */
/* if(doclip && t0<t2) { */
/* *intens *= (t0-t1)/(t2-t1); */
/* } */
*intens *= haint;
if(lar->shb && lar->shb->shadhalostep) {
*intens *= shadow_halo(lar, p1, p2);
}
}
}
static void renderspothalo(ShadeInput *shi, float *col, float alpha)
{
GroupObject *go;
LampRen *lar;
float i;
if(alpha==0.0f) return;
for(go=R.lights.first; go; go= go->next) {
lar= go->lampren;
if(lar->type==LA_SPOT && (lar->mode & LA_HALO) && lar->haint>0) {
spothalo(lar, shi, &i);
if(i>0.0) {
col[3]+= i*alpha; // all premul
col[0]+= i*lar->r*alpha;
col[1]+= i*lar->g*alpha;
col[2]+= i*lar->b*alpha;
}
}
}
/* clip alpha, is needed for unified 'alpha threshold' (vanillaRenderPipe.c) */
if(col[3]>1.0) col[3]= 1.0;
}
static int calchalo_z(HaloRen *har, int zz)
{
if(har->type & HA_ONLYSKY) {
if(zz!=0x7FFFFFFF) zz= - 0x7FFFFF;
}
else {
zz= (zz>>8);
}
return zz;
}
static void scanlinehaloPS(int *rectz, long *rectdelta, float *rowbuf, short ys)
{
HaloRen *har = NULL;
PixStr *ps;
float dist, xsq, ysq, xn, yn;
float *rb;
float col[4], accol[4];
int a, *rz, zz, didgamma=0;
long *rd;
short minx, maxx, x, amount, amountm, flarec;
for(a=0; a<R.tothalo; a++) {
if((a & 255)==0) {
har= R.bloha[a>>8];
if( RE_local_test_break() ) break;
}
else har++;
if(ys>har->maxy);
else if(ys<har->miny);
else {
minx= floor(har->xs-har->rad);
maxx= ceil(har->xs+har->rad);
if(maxx<0);
else if(R.rectx<minx);
else {
if(minx<0) minx= 0;
if(maxx>=R.rectx) maxx= R.rectx-1;
rb= rowbuf + 4*minx;
rd= rectdelta + minx;
rz= rectz + minx;
yn= (ys-har->ys)*R.ycor;
ysq= yn*yn;
for(x=minx; x<=maxx; x++) {
xn= x-har->xs;
xsq= xn*xn;
dist= xsq+ysq;
if(dist<har->radsq) {
/* well yah, halo adding shouldnt be done gamma corrected, have to bypass it this way */
/* alternative is moving it outside of thread renderlineDA */
/* on positive side; the invert correct cancels out correcting halo color */
if(do_gamma && didgamma==0) {
float *buf= rowbuf;
int xt;
for(xt=0; xt<R.rectx; xt++, buf+=4) {
buf[0]= sqrt(buf[0]); // invers gamma 2.0
buf[1]= sqrt(buf[1]);
buf[2]= sqrt(buf[2]);
}
didgamma= 1;
}
flarec= har->flarec; /* har->pixels is only allowed to count once */
if(*rd) { /* theres a pixel struct */
ps= (PixStr *)(*rd);
amount= 0;
accol[0]=accol[1]=accol[2]=accol[3]= 0.0;
while(ps) {
amountm= count_mask(ps->mask);
amount+= amountm;
zz= calchalo_z(har, ps->z);
if(zz> har->zs) {
float fac;
shadeHaloFloat(har, col, zz, dist, xn, yn, flarec);
fac= ((float)amountm)/(float)R.osa;
accol[0]+= fac*col[0];
accol[1]+= fac*col[1];
accol[2]+= fac*col[2];
accol[3]+= fac*col[3];
flarec= 0;
}
ps= ps->next;
}
/* now do the sky sub-pixels */
amount= R.osa-amount;
if(amount) {
float fac;
shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec);
fac= ((float)amount)/(float)R.osa;
accol[0]+= fac*col[0];
accol[1]+= fac*col[1];
accol[2]+= fac*col[2];
accol[3]+= fac*col[3];
}
col[0]= accol[0];
col[1]= accol[1];
col[2]= accol[2];
col[3]= accol[3];
addalphaAddfacFloat(rb, col, har->add);
}
else {
zz= calchalo_z(har, *rz);
if(zz> har->zs) {
shadeHaloFloat(har, col, zz, dist, xn, yn, flarec);
addalphaAddfacFloat(rb, col, har->add);
}
}
}
rb+=4;
rz++;
rd++;
}
}
}
}
/* the entire scanline has to be put back in gammaspace */
if(didgamma) {
float *buf= rowbuf;
int xt;
for(xt=0; xt<R.rectx; xt++, buf+=4) {
buf[0]*= (buf[0]); // gamma 2.0
buf[1]*= (buf[1]);
buf[2]*= (buf[2]);
}
}
}
static void scanlinehalo(int *rectz, float *rowbuf, short ys)
{
HaloRen *har = NULL;
float dist, xsq, ysq, xn, yn, *rb;
float col[4];
int a, *rz, zz;
short minx, maxx, x;
for(a=0; a<R.tothalo; a++) {
if((a & 255)==0) har= R.bloha[a>>8];
else har++;
if(RE_local_test_break() ) break;
if(ys>har->maxy);
else if(ys<har->miny);
else {
minx= floor(har->xs-har->rad);
maxx= ceil(har->xs+har->rad);
if(maxx<0);
else if(R.rectx<minx);
else {
if(minx<0) minx= 0;
if(maxx>=R.rectx) maxx= R.rectx-1;
rb= rowbuf + 4*minx;
rz= rectz + minx;
yn= (ys-har->ys)*R.ycor;
ysq= yn*yn;
for(x=minx; x<=maxx; x++) {
zz= calchalo_z(har, *rz);
if(zz> har->zs) {
xn= x- har->xs;
xsq= xn*xn;
dist= xsq+ysq;
if(dist<har->radsq) {
shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec);
addalphaAddfacFloat(rb, col, har->add);
}
}
rb+=4;
rz++;
}
}
}
}
}
/* ---------------- shaders ----------------------- */
static double Normalise_d(double *n)
{
double d;
d= n[0]*n[0]+n[1]*n[1]+n[2]*n[2];
if(d>0.00000000000000001) {
d= sqrt(d);
n[0]/=d;
n[1]/=d;
n[2]/=d;
} else {
n[0]=n[1]=n[2]= 0.0;
d= 0.0;
}
return d;
}
/* mix of 'real' fresnel and allowing control. grad defines blending gradient */
float fresnel_fac(float *view, float *vn, float grad, float fac)
{
float t1, t2;
if(fac==0.0) return 1.0;
t1= (view[0]*vn[0] + view[1]*vn[1] + view[2]*vn[2]);
if(t1>0.0) t2= 1.0+t1;
else t2= 1.0-t1;
t2= grad + (1.0-grad)*pow(t2, fac);
if(t2<0.0) return 0.0;
else if(t2>1.0) return 1.0;
return t2;
}
static double saacos_d(double fac)
{
if(fac<= -1.0f) return M_PI;
else if(fac>=1.0f) return 0.0;
else return acos(fac);
}
/* Stoke's form factor. Need doubles here for extreme small area sizes */
static float area_lamp_energy(float *co, float *vn, LampRen *lar)
{
double fac;
double vec[4][3]; /* vectors of rendered co to vertices lamp */
double cross[4][3]; /* cross products of this */
double rad[4]; /* angles between vecs */
VECSUB(vec[0], co, lar->area[0]);
VECSUB(vec[1], co, lar->area[1]);
VECSUB(vec[2], co, lar->area[2]);
VECSUB(vec[3], co, lar->area[3]);
Normalise_d(vec[0]);
Normalise_d(vec[1]);
Normalise_d(vec[2]);
Normalise_d(vec[3]);
/* cross product */
CROSS(cross[0], vec[0], vec[1]);
CROSS(cross[1], vec[1], vec[2]);
CROSS(cross[2], vec[2], vec[3]);
CROSS(cross[3], vec[3], vec[0]);
Normalise_d(cross[0]);
Normalise_d(cross[1]);
Normalise_d(cross[2]);
Normalise_d(cross[3]);
/* angles */
rad[0]= vec[0][0]*vec[1][0]+ vec[0][1]*vec[1][1]+ vec[0][2]*vec[1][2];
rad[1]= vec[1][0]*vec[2][0]+ vec[1][1]*vec[2][1]+ vec[1][2]*vec[2][2];
rad[2]= vec[2][0]*vec[3][0]+ vec[2][1]*vec[3][1]+ vec[2][2]*vec[3][2];
rad[3]= vec[3][0]*vec[0][0]+ vec[3][1]*vec[0][1]+ vec[3][2]*vec[0][2];
rad[0]= saacos_d(rad[0]);
rad[1]= saacos_d(rad[1]);
rad[2]= saacos_d(rad[2]);
rad[3]= saacos_d(rad[3]);
/* Stoke formula */
fac= rad[0]*(vn[0]*cross[0][0]+ vn[1]*cross[0][1]+ vn[2]*cross[0][2]);
fac+= rad[1]*(vn[0]*cross[1][0]+ vn[1]*cross[1][1]+ vn[2]*cross[1][2]);
fac+= rad[2]*(vn[0]*cross[2][0]+ vn[1]*cross[2][1]+ vn[2]*cross[2][2]);
fac+= rad[3]*(vn[0]*cross[3][0]+ vn[1]*cross[3][1]+ vn[2]*cross[3][2]);
if(fac<=0.0) return 0.0;
return pow(fac*lar->areasize, lar->k); // corrected for buttons size and lar->dist^2
}
float spec(float inp, int hard)
{
float b1;
if(inp>=1.0) return 1.0;
else if (inp<=0.0) return 0.0;
b1= inp*inp;
/* avoid FPE */
if(b1<0.01) b1= 0.01;
if((hard & 1)==0) inp= 1.0;
if(hard & 2) inp*= b1;
b1*= b1;
if(hard & 4) inp*= b1;
b1*= b1;
if(hard & 8) inp*= b1;
b1*= b1;
if(hard & 16) inp*= b1;
b1*= b1;
/* avoid FPE */
if(b1<0.001) b1= 0.0;
if(hard & 32) inp*= b1;
b1*= b1;
if(hard & 64) inp*=b1;
b1*= b1;
if(hard & 128) inp*=b1;
if(b1<0.001) b1= 0.0;
if(hard & 256) {
b1*= b1;
inp*=b1;
}
return inp;
}
float Phong_Spec( float *n, float *l, float *v, int hard, int tangent )
{
float h[3];
float rslt;
h[0] = l[0] + v[0];
h[1] = l[1] + v[1];
h[2] = l[2] + v[2];
Normalise(h);
rslt = h[0]*n[0] + h[1]*n[1] + h[2]*n[2];
if(tangent) rslt= sasqrt(1.0 - rslt*rslt);
if( rslt > 0.0 ) rslt= spec(rslt, hard);
else rslt = 0.0;
return rslt;
}
/* reduced cook torrance spec (for off-specular peak) */
float CookTorr_Spec(float *n, float *l, float *v, int hard, int tangent)
{
float i, nh, nv, h[3];
h[0]= v[0]+l[0];
h[1]= v[1]+l[1];
h[2]= v[2]+l[2];
Normalise(h);
nh= n[0]*h[0]+n[1]*h[1]+n[2]*h[2];
if(tangent) nh= sasqrt(1.0 - nh*nh);
else if(nh<0.0) return 0.0;
nv= n[0]*v[0]+n[1]*v[1]+n[2]*v[2];
if(tangent) nv= sasqrt(1.0 - nv*nv);
else if(nv<0.0) nv= 0.0;
i= spec(nh, hard);
i= i/(0.1+nv);
return i;
}
/* Blinn spec */
float Blinn_Spec(float *n, float *l, float *v, float refrac, float spec_power, int tangent)
{
float i, nh, nv, nl, vh, h[3];
float a, b, c, g=0.0, p, f, ang;
if(refrac < 1.0) return 0.0;
if(spec_power == 0.0) return 0.0;
/* conversion from 'hardness' (1-255) to 'spec_power' (50 maps at 0.1) */
if(spec_power<100.0)
spec_power= sqrt(1.0/spec_power);
else spec_power= 10.0/spec_power;
h[0]= v[0]+l[0];
h[1]= v[1]+l[1];
h[2]= v[2]+l[2];
Normalise(h);
nh= n[0]*h[0]+n[1]*h[1]+n[2]*h[2]; /* Dot product between surface normal and half-way vector */
if(tangent) nh= sasqrt(1.0f - nh*nh);
else if(nh<0.0) return 0.0;
nv= n[0]*v[0]+n[1]*v[1]+n[2]*v[2]; /* Dot product between surface normal and view vector */
if(tangent) nv= sasqrt(1.0f - nv*nv);
if(nv<=0.0) nv= 0.01; /* hrms... */
nl= n[0]*l[0]+n[1]*l[1]+n[2]*l[2]; /* Dot product between surface normal and light vector */
if(tangent) nl= sasqrt(1.0f - nl*nl);
if(nl<=0.0) {
return 0.0;
}
vh= v[0]*h[0]+v[1]*h[1]+v[2]*h[2]; /* Dot product between view vector and half-way vector */
if(vh<=0.0) vh= 0.01;
a = 1.0;
b = (2.0*nh*nv)/vh;
c = (2.0*nh*nl)/vh;
if( a < b && a < c ) g = a;
else if( b < a && b < c ) g = b;
else if( c < a && c < b ) g = c;
p = sqrt( (double)((refrac * refrac)+(vh*vh)-1.0) );
f = (((p-vh)*(p-vh))/((p+vh)*(p+vh)))*(1+((((vh*(p+vh))-1.0)*((vh*(p+vh))-1.0))/(((vh*(p-vh))+1.0)*((vh*(p-vh))+1.0))));
ang = saacos(nh);
i= f * g * exp((double)(-(ang*ang) / (2.0*spec_power*spec_power)));
if(i<0.0) i= 0.0;
return i;
}
/* cartoon render spec */
float Toon_Spec( float *n, float *l, float *v, float size, float smooth, int tangent)
{
float h[3];
float ang;
float rslt;
h[0] = l[0] + v[0];
h[1] = l[1] + v[1];
h[2] = l[2] + v[2];
Normalise(h);
rslt = h[0]*n[0] + h[1]*n[1] + h[2]*n[2];
if(tangent) rslt = sasqrt(1.0f - rslt*rslt);
ang = saacos( rslt );
if( ang < size ) rslt = 1.0;
else if( ang >= (size + smooth) || smooth == 0.0 ) rslt = 0.0;
else rslt = 1.0 - ((ang - size) / smooth);
return rslt;
}
/* Ward isotropic gaussian spec */
float WardIso_Spec( float *n, float *l, float *v, float rms, int tangent)
{
float i, nh, nv, nl, h[3], angle, alpha;
/* half-way vector */
h[0] = l[0] + v[0];
h[1] = l[1] + v[1];
h[2] = l[2] + v[2];
Normalise(h);
nh = n[0]*h[0]+n[1]*h[1]+n[2]*h[2]; /* Dot product between surface normal and half-way vector */
if(tangent) nh = sasqrt(1.0f - nh*nh);
if(nh<=0.0) nh = 0.001f;
nv = n[0]*v[0]+n[1]*v[1]+n[2]*v[2]; /* Dot product between surface normal and view vector */
if(tangent) nv = sasqrt(1.0f - nv*nv);
if(nv<=0.0) nv = 0.001f;
nl = n[0]*l[0]+n[1]*l[1]+n[2]*l[2]; /* Dot product between surface normal and light vector */
if(tangent) nl = sasqrt(1.0f - nl*nl);
if(nl<=0.0) nl = 0.001;
angle = tan(saacos(nh));
alpha = MAX2(rms,0.001);
i= nl * (1.0/(4*PI*alpha*alpha)) * (exp( -(angle*angle)/(alpha*alpha))/(sqrt(nv*nl)));
return i;
}
/* cartoon render diffuse */
float Toon_Diff( float *n, float *l, float *v, float size, float smooth )
{
float rslt, ang;
rslt = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
ang = saacos( (double)(rslt) );
if( ang < size ) rslt = 1.0;
else if( ang >= (size + smooth) || smooth == 0.0 ) rslt = 0.0;
else rslt = 1.0 - ((ang - size) / smooth);
return rslt;
}
/* Oren Nayar diffuse */
/* 'nl' is either dot product, or return value of area light */
/* in latter case, only last multiplication uses 'nl' */
static float OrenNayar_Diff_i(float nl, float *n, float *l, float *v, float rough )
{
float i, nh, nv, vh, realnl, h[3];
float a, b, t, A, B;
float Lit_A, View_A, Lit_B[3], View_B[3];
h[0]= v[0]+l[0];
h[1]= v[1]+l[1];
h[2]= v[2]+l[2];
Normalise(h);
nh= n[0]*h[0]+n[1]*h[1]+n[2]*h[2]; /* Dot product between surface normal and half-way vector */
if(nh<0.0) nh = 0.0;
nv= n[0]*v[0]+n[1]*v[1]+n[2]*v[2]; /* Dot product between surface normal and view vector */
if(nv<=0.0) nv= 0.0;
realnl= n[0]*l[0]+n[1]*l[1]+n[2]*l[2]; /* Dot product between surface normal and light vector */
if(realnl<=0.0) return 0.0;
if(nl<0.0) return 0.0; /* value from area light */
vh= v[0]*h[0]+v[1]*h[1]+v[2]*h[2]; /* Dot product between view vector and halfway vector */
if(vh<=0.0) vh= 0.0;
Lit_A = saacos(realnl);
View_A = saacos( nv );
Lit_B[0] = l[0] - (realnl * n[0]);
Lit_B[1] = l[1] - (realnl * n[1]);
Lit_B[2] = l[2] - (realnl * n[2]);
Normalise( Lit_B );
View_B[0] = v[0] - (nv * n[0]);
View_B[1] = v[1] - (nv * n[1]);
View_B[2] = v[2] - (nv * n[2]);
Normalise( View_B );
t = Lit_B[0]*View_B[0] + Lit_B[1]*View_B[1] + Lit_B[2]*View_B[2];
if( t < 0 ) t = 0;
if( Lit_A > View_A ) {
a = Lit_A;
b = View_A;
}
else {
a = View_A;
b = Lit_A;
}
A = 1 - (0.5 * ((rough * rough) / ((rough * rough) + 0.33)));
B = 0.45 * ((rough * rough) / ((rough * rough) + 0.09));
b*= 0.95; /* prevent tangens from shooting to inf, 'nl' can be not a dot product here. */
/* overflow only happens with extreme size area light, and higher roughness */
i = nl * ( A + ( B * t * sin(a) * tan(b) ) );
return i;
}
/* Oren Nayar diffuse */
float OrenNayar_Diff(float *n, float *l, float *v, float rough )
{
float nl= n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
return OrenNayar_Diff_i(nl, n, l, v, rough);
}
/* Minnaert diffuse */
float Minnaert_Diff(float nl, float *n, float *v, float darkness)
{
float i, nv;
/* nl = dot product between surface normal and light vector */
if (nl <= 0.0)
return 0;
/* nv = dot product between surface normal and view vector */
nv = n[0]*v[0]+n[1]*v[1]+n[2]*v[2];
if (nv < 0.0)
nv = 0;
if (darkness <= 1)
i = nl * pow(MAX2(nv*nl, 0.1), (darkness - 1) ); /*The Real model*/
else
i = nl * pow( (1.001 - nv), (darkness - 1) ); /*Nvidia model*/
return i;
}
float Fresnel_Diff(float *vn, float *lv, float *view, float fac_i, float fac)
{
return fresnel_fac(lv, vn, fac_i, fac);
}
/* --------------------------------------------- */
/* also called from texture.c */
void calc_R_ref(ShadeInput *shi)
{
float i;
/* shi->vn dot shi->view */
i= -2*(shi->vn[0]*shi->view[0]+shi->vn[1]*shi->view[1]+shi->vn[2]*shi->view[2]);
shi->ref[0]= (shi->view[0]+i*shi->vn[0]);
shi->ref[1]= (shi->view[1]+i*shi->vn[1]);
shi->ref[2]= (shi->view[2]+i*shi->vn[2]);
if(shi->osatex) {
if(shi->vlr->flag & R_SMOOTH) {
i= -2*( (shi->vn[0]+shi->dxno[0])*(shi->view[0]+shi->dxview) +
(shi->vn[1]+shi->dxno[1])*shi->view[1]+ (shi->vn[2]+shi->dxno[2])*shi->view[2] );
shi->dxref[0]= shi->ref[0]- ( shi->view[0]+shi->dxview+i*(shi->vn[0]+shi->dxno[0]));
shi->dxref[1]= shi->ref[1]- (shi->view[1]+ i*(shi->vn[1]+shi->dxno[1]));
shi->dxref[2]= shi->ref[2]- (shi->view[2]+ i*(shi->vn[2]+shi->dxno[2]));
i= -2*( (shi->vn[0]+shi->dyno[0])*shi->view[0]+
(shi->vn[1]+shi->dyno[1])*(shi->view[1]+shi->dyview)+ (shi->vn[2]+shi->dyno[2])*shi->view[2] );
shi->dyref[0]= shi->ref[0]- (shi->view[0]+ i*(shi->vn[0]+shi->dyno[0]));
shi->dyref[1]= shi->ref[1]- (shi->view[1]+shi->dyview+i*(shi->vn[1]+shi->dyno[1]));
shi->dyref[2]= shi->ref[2]- (shi->view[2]+ i*(shi->vn[2]+shi->dyno[2]));
}
else {
i= -2*( shi->vn[0]*(shi->view[0]+shi->dxview) +
shi->vn[1]*shi->view[1]+ shi->vn[2]*shi->view[2] );
shi->dxref[0]= shi->ref[0]- (shi->view[0]+shi->dxview+i*shi->vn[0]);
shi->dxref[1]= shi->ref[1]- (shi->view[1]+ i*shi->vn[1]);
shi->dxref[2]= shi->ref[2]- (shi->view[2]+ i*shi->vn[2]);
i= -2*( shi->vn[0]*shi->view[0]+
shi->vn[1]*(shi->view[1]+shi->dyview)+ shi->vn[2]*shi->view[2] );
shi->dyref[0]= shi->ref[0]- (shi->view[0]+ i*shi->vn[0]);
shi->dyref[1]= shi->ref[1]- (shi->view[1]+shi->dyview+i*shi->vn[1]);
shi->dyref[2]= shi->ref[2]- (shi->view[2]+ i*shi->vn[2]);
}
}
}
void shade_color(ShadeInput *shi, ShadeResult *shr)
{
Material *ma= shi->mat;
if(ma->mode & (MA_VERTEXCOLP|MA_FACETEXTURE)) {
shi->r= shi->vcol[0];
shi->g= shi->vcol[1];
shi->b= shi->vcol[2];
}
if(ma->texco) {
if(ma->mode & (MA_VERTEXCOLP|MA_FACETEXTURE)) {
shi->r= shi->vcol[0];
shi->g= shi->vcol[1];
shi->b= shi->vcol[2];
}
do_material_tex(shi);
}
if(ma->mode & (MA_ZTRA|MA_RAYTRANSP)) {
if(ma->fresnel_tra!=0.0)
shi->alpha*= fresnel_fac(shi->view, shi->vn, ma->fresnel_tra_i, ma->fresnel_tra);
}
shr->diff[0]= shi->r;
shr->diff[1]= shi->g;
shr->diff[2]= shi->b;
shr->alpha= shi->alpha;
}
/* r g b = current value, col = new value, fac==0 is no change */
/* if g==NULL, it only does r channel */
void ramp_blend(int type, float *r, float *g, float *b, float fac, float *col)
{
float tmp, facm= 1.0-fac;
switch (type) {
case MA_RAMP_BLEND:
*r = facm*(*r) + fac*col[0];
if(g) {
*g = facm*(*g) + fac*col[1];
*b = facm*(*b) + fac*col[2];
}
break;
case MA_RAMP_ADD:
*r += fac*col[0];
if(g) {
*g += fac*col[1];
*b += fac*col[2];
}
break;
case MA_RAMP_MULT:
*r *= (facm + fac*col[0]);
if(g) {
*g *= (facm + fac*col[1]);
*b *= (facm + fac*col[2]);
}
break;
case MA_RAMP_SCREEN:
*r = 1.0 - (facm + fac*(1.0 - col[0])) * (1.0 - *r);
if(g) {
*g = 1.0 - (facm + fac*(1.0 - col[1])) * (1.0 - *g);
*b = 1.0 - (facm + fac*(1.0 - col[2])) * (1.0 - *b);
}
break;
case MA_RAMP_SUB:
*r -= fac*col[0];
if(g) {
*g -= fac*col[1];
*b -= fac*col[2];
}
break;
case MA_RAMP_DIV:
if(col[0]!=0.0)
*r = facm*(*r) + fac*(*r)/col[0];
if(g) {
if(col[1]!=0.0)
*g = facm*(*g) + fac*(*g)/col[1];
if(col[2]!=0.0)
*b = facm*(*b) + fac*(*b)/col[2];
}
break;
case MA_RAMP_DIFF:
*r = facm*(*r) + fac*fabs(*r-col[0]);
if(g) {
*g = facm*(*g) + fac*fabs(*g-col[1]);
*b = facm*(*b) + fac*fabs(*b-col[2]);
}
break;
case MA_RAMP_DARK:
tmp= fac*col[0];
if(tmp < *r) *r= tmp;
if(g) {
tmp= fac*col[1];
if(tmp < *g) *g= tmp;
tmp= fac*col[2];
if(tmp < *b) *b= tmp;
}
break;
case MA_RAMP_LIGHT:
tmp= fac*col[0];
if(tmp > *r) *r= tmp;
if(g) {
tmp= fac*col[1];
if(tmp > *g) *g= tmp;
tmp= fac*col[2];
if(tmp > *b) *b= tmp;
}
break;
}
}
/* ramp for at end of shade */
void ramp_diffuse_result(float *diff, ShadeInput *shi)
{
Material *ma= shi->mat;
float col[4], fac=0;
if(ma->ramp_col) {
if(ma->rampin_col==MA_RAMP_IN_RESULT) {
fac= 0.3*diff[0] + 0.58*diff[1] + 0.12*diff[2];
do_colorband(ma->ramp_col, fac, col);
/* blending method */
fac= col[3]*ma->rampfac_col;
ramp_blend(ma->rampblend_col, diff, diff+1, diff+2, fac, col);
}
}
}
/* r,g,b denote energy, ramp is used with different values to make new material color */
void add_to_diffuse(float *diff, ShadeInput *shi, float is, float r, float g, float b)
{
Material *ma= shi->mat;
float col[4], colt[3], fac=0;
if(ma->ramp_col && (ma->mode & MA_RAMP_COL)) {
/* MA_RAMP_IN_RESULT is exceptional */
if(ma->rampin_col==MA_RAMP_IN_RESULT) {
// normal add
diff[0] += r * shi->r;
diff[1] += g * shi->g;
diff[2] += b * shi->b;
}
else {
/* input */
switch(ma->rampin_col) {
case MA_RAMP_IN_ENERGY:
fac= 0.3*r + 0.58*g + 0.12*b;
break;
case MA_RAMP_IN_SHADER:
fac= is;
break;
case MA_RAMP_IN_NOR:
fac= shi->view[0]*shi->vn[0] + shi->view[1]*shi->vn[1] + shi->view[2]*shi->vn[2];
break;
}
do_colorband(ma->ramp_col, fac, col);
/* blending method */
fac= col[3]*ma->rampfac_col;
colt[0]= shi->r; colt[1]= shi->g; colt[2]= shi->b;
ramp_blend(ma->rampblend_col, colt, colt+1, colt+2, fac, col);
/* output to */
diff[0] += r * colt[0];
diff[1] += g * colt[1];
diff[2] += b * colt[2];
}
}
else {
diff[0] += r * shi->r;
diff[1] += g * shi->g;
diff[2] += b * shi->b;
}
}
void ramp_spec_result(float *specr, float *specg, float *specb, ShadeInput *shi)
{
Material *ma= shi->mat;
float col[4];
float fac;
if(ma->ramp_spec && (ma->rampin_spec==MA_RAMP_IN_RESULT)) {
fac= 0.3*(*specr) + 0.58*(*specg) + 0.12*(*specb);
do_colorband(ma->ramp_spec, fac, col);
/* blending method */
fac= col[3]*ma->rampfac_spec;
ramp_blend(ma->rampblend_spec, specr, specg, specb, fac, col);
}
}
/* is = dot product shade, t = spec energy */
void do_specular_ramp(ShadeInput *shi, float is, float t, float *spec)
{
Material *ma= shi->mat;
float col[4];
float fac=0.0;
spec[0]= shi->specr;
spec[1]= shi->specg;
spec[2]= shi->specb;
/* MA_RAMP_IN_RESULT is exception */
if(ma->ramp_spec && (ma->rampin_spec!=MA_RAMP_IN_RESULT)) {
/* input */
switch(ma->rampin_spec) {
case MA_RAMP_IN_ENERGY:
fac= t;
break;
case MA_RAMP_IN_SHADER:
fac= is;
break;
case MA_RAMP_IN_NOR:
fac= shi->view[0]*shi->vn[0] + shi->view[1]*shi->vn[1] + shi->view[2]*shi->vn[2];
break;
}
do_colorband(ma->ramp_spec, fac, col);
/* blending method */
fac= col[3]*ma->rampfac_spec;
ramp_blend(ma->rampblend_spec, spec, spec+1, spec+2, fac, col);
}
}
static void ambient_occlusion(World *wrld, ShadeInput *shi, ShadeResult *shr)
{
float f, shadfac[4];
if((wrld->mode & WO_AMB_OCC) && (R.r.mode & R_RAYTRACE) && shi->amb!=0.0) {
ray_ao(shi, wrld, shadfac);
if(wrld->aocolor==WO_AOPLAIN) {
if (wrld->aomix==WO_AOADDSUB) shadfac[3] = 2.0*shadfac[3]-1.0;
else if (wrld->aomix==WO_AOSUB) shadfac[3] = shadfac[3]-1.0;
f= wrld->aoenergy*shadfac[3]*shi->amb;
add_to_diffuse(shr->diff, shi, f, f, f, f);
}
else {
if (wrld->aomix==WO_AOADDSUB) {
shadfac[0] = 2.0*shadfac[0]-1.0;
shadfac[1] = 2.0*shadfac[1]-1.0;
shadfac[2] = 2.0*shadfac[2]-1.0;
}
else if (wrld->aomix==WO_AOSUB) {
shadfac[0] = shadfac[0]-1.0;
shadfac[1] = shadfac[1]-1.0;
shadfac[2] = shadfac[2]-1.0;
}
f= wrld->aoenergy*shi->amb;
add_to_diffuse(shr->diff, shi, f, f*shadfac[0], f*shadfac[1], f*shadfac[2]);
}
}
}
void shade_lamp_loop(ShadeInput *shi, ShadeResult *shr)
{
LampRen *lar;
GroupObject *go;
Material *ma= shi->mat;
VlakRen *vlr= shi->vlr;
ListBase *lights;
float i, inp, inpr, is, t, lv[3], vnor[3], lacol[3], lampdist, ld = 0;
float lvrot[3], *vn, *view, shadfac[4], soft, phongcorr; // shadfac = rgba
vn= shi->vn;
view= shi->view;
memset(shr, 0, sizeof(ShadeResult));
/* copy all relevant material vars, note, keep this synced with render_types.h */
memcpy(&shi->r, &ma->r, 23*sizeof(float));
/* set special cases */
shi->har= ma->har;
if((ma->mode & MA_RAYMIRROR)==0) shi->ray_mirror= 0.0;
/* lights */
if(ma->group)
lights= &ma->group->gobject;
else
lights= &R.lights;
/* separate loop */
if(ma->mode & MA_ONLYSHADOW) {
float ir;
if(R.r.mode & R_SHADOW) {
shadfac[3]= ir= 0.0;
for(go=lights->first; go; go= go->next) {
lar= go->lampren;
if(lar==NULL) continue;
/* yafray: ignore shading by photonlights, not used in Blender */
if (lar->type==LA_YF_PHOTON) continue;
if(lar->mode & LA_LAYER) if((lar->lay & vlr->lay)==0) continue;
lv[0]= shi->co[0]-lar->co[0];
lv[1]= shi->co[1]-lar->co[1];
lv[2]= shi->co[2]-lar->co[2];
if(lar->type==LA_SPOT) {
/* only test within spotbundel */
if(lar->shb || (lar->mode & LA_SHAD_RAY)) {
Normalise(lv);
inpr= lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2];
if(inpr>lar->spotsi) {
inp= vn[0]*lv[0] + vn[1]*lv[1] + vn[2]*lv[2];
if(lar->shb) i = testshadowbuf(lar->shb, shi->co, shi->dxco, shi->dyco, inp);
else {
float shad[4];
ray_shadow(shi, lar, shad);
i= shad[3];
}
t= inpr - lar->spotsi;
if(t<lar->spotbl && lar->spotbl!=0.0) {
t/= lar->spotbl;
t*= t;
i= t*i+(1.0-t);
}
shadfac[3]+= i;
ir+= 1.0;
}
else {
shadfac[3]+= 1.0;
ir+= 1.0;
}
}
}
else if(lar->mode & LA_SHAD_RAY) {
float shad[4];
/* single sided? */
if( shi->facenor[0]*lv[0] + shi->facenor[1]*lv[1] + shi->facenor[2]*lv[2] > -0.01) {
ray_shadow(shi, lar, shad);
shadfac[3]+= shad[3];
ir+= 1.0;
}
}
}
if(ir>0.0) {
shadfac[3]/= ir;
shr->alpha= (shi->alpha)*(1.0-shadfac[3]);
}
}
if((R.wrld.mode & WO_AMB_OCC) && (R.r.mode & R_RAYTRACE) && shi->amb!=0.0) {
float f;
ray_ao(shi, &R.wrld, shadfac); // shadfac==0: full light
shadfac[3]= 1.0-shadfac[3];
f= R.wrld.aoenergy*shadfac[3]*shi->amb;
if(R.wrld.aomix==WO_AOADD) {
shr->alpha += f;
shr->alpha *= f;
}
else if(R.wrld.aomix==WO_AOSUB) {
shr->alpha += f;
}
else {
shr->alpha *= f;
shr->alpha += f;
}
}
return;
}
if(ma->mode & (MA_VERTEXCOLP|MA_FACETEXTURE)) {
shi->r= shi->vcol[0];
shi->g= shi->vcol[1];
shi->b= shi->vcol[2];
}
/* envmap hack, always reset */
shi->refcol[0]= shi->refcol[1]= shi->refcol[2]= shi->refcol[3]= 0.0;
if(ma->texco) {
if(ma->mode & (MA_VERTEXCOLP|MA_FACETEXTURE)) {
shi->r= shi->vcol[0];
shi->g= shi->vcol[1];
shi->b= shi->vcol[2];
}
do_material_tex(shi);
}
if(ma->mode & MA_SHLESS) {
shr->diff[0]= shi->r;
shr->diff[1]= shi->g;
shr->diff[2]= shi->b;
shr->alpha= shi->alpha;
return;
}
if( (ma->mode & (MA_VERTEXCOL|MA_VERTEXCOLP))== MA_VERTEXCOL ) { // vertexcolor light
// add_to_diffuse(shr->diff, shi, 1.0, ma->emit+shi->vcol[0], ma->emit+shi->vcol[1], ma->emit+shi->vcol[2]);
shr->diff[0]= shi->r*(shi->emit+shi->vcol[0]);
shr->diff[1]= shi->g*(shi->emit+shi->vcol[1]);
shr->diff[2]= shi->b*(shi->emit+shi->vcol[2]);
}
else {
// add_to_diffuse(shr->diff, shi, 1.0, ma->emit, ma->emit, ma->emit);
shr->diff[0]= shi->r*shi->emit;
shr->diff[1]= shi->g*shi->emit;
shr->diff[2]= shi->b*shi->emit;
}
ambient_occlusion(&R.wrld, shi, shr);
for(go=lights->first; go; go= go->next) {
lar= go->lampren;
if(lar==NULL) continue;
/* yafray: ignore shading by photonlights, not used in Blender */
if (lar->type==LA_YF_PHOTON) continue;
/* test for lamp layer */
if(lar->mode & LA_LAYER) if((lar->lay & vlr->lay)==0) continue;
/* lampdist calculation */
if(lar->type==LA_SUN || lar->type==LA_HEMI) {
VECCOPY(lv, lar->vec);
lampdist= 1.0;
}
else {
lv[0]= shi->co[0]-lar->co[0];
lv[1]= shi->co[1]-lar->co[1];
lv[2]= shi->co[2]-lar->co[2];
ld= sqrt(lv[0]*lv[0]+lv[1]*lv[1]+lv[2]*lv[2]);
lv[0]/= ld;
lv[1]/= ld;
lv[2]/= ld;
/* ld is re-used further on (texco's) */
if(lar->type==LA_AREA) {
lampdist= 1.0;
}
else {
if(lar->mode & LA_QUAD) {
t= 1.0;
if(lar->ld1>0.0)
t= lar->dist/(lar->dist+lar->ld1*ld);
if(lar->ld2>0.0)
t*= lar->distkw/(lar->distkw+lar->ld2*ld*ld);
lampdist= t;
}
else {
lampdist= (lar->dist/(lar->dist+ld));
}
if(lar->mode & LA_SPHERE) {
t= lar->dist - ld;
if(t<0.0) continue;
t/= lar->dist;
lampdist*= (t);
}
}
}
lacol[0]= lar->r;
lacol[1]= lar->g;
lacol[2]= lar->b;
/* init transp shadow */
shadfac[3]= 1.0;
if(ma->mode & MA_SHADOW_TRA) shadfac[0]= shadfac[1]= shadfac[2]= 1.0;
if(lar->type==LA_SPOT) {
if(lar->mode & LA_SQUARE) {
if(lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2]>0.0) {
float x;
/* rotate view to lampspace */
VECCOPY(lvrot, lv);
MTC_Mat3MulVecfl(lar->imat, lvrot);
x= MAX2(fabs(lvrot[0]/lvrot[2]) , fabs(lvrot[1]/lvrot[2]));
/* 1.0/(sqrt(1+x*x)) is equivalent to cos(atan(x)) */
inpr= 1.0f/(sqrt(1.0f+x*x));
}
else inpr= 0.0;
}
else {
inpr= lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2];
}
t= lar->spotsi;
if(inpr<t) continue;
else {
t= inpr-t;
i= 1.0;
soft= 1.0;
if(t<lar->spotbl && lar->spotbl!=0.0) {
/* soft area */
i= t/lar->spotbl;
t= i*i;
soft= (3.0*t-2.0*t*i);
inpr*= soft;
}
lampdist*=inpr;
}
if(lar->mode & LA_OSATEX) {
shi->osatex= 1; /* signal for multitex() */
shi->dxlv[0]= lv[0] - (shi->co[0]-lar->co[0]+shi->dxco[0])/ld;
shi->dxlv[1]= lv[1] - (shi->co[1]-lar->co[1]+shi->dxco[1])/ld;
shi->dxlv[2]= lv[2] - (shi->co[2]-lar->co[2]+shi->dxco[2])/ld;
shi->dylv[0]= lv[0] - (shi->co[0]-lar->co[0]+shi->dyco[0])/ld;
shi->dylv[1]= lv[1] - (shi->co[1]-lar->co[1]+shi->dyco[1])/ld;
shi->dylv[2]= lv[2] - (shi->co[2]-lar->co[2]+shi->dyco[2])/ld;
}
}
if(lar->mode & LA_TEXTURE) do_lamp_tex(lar, lv, shi, lacol);
/* dot product and reflectivity */
/* inp = dotproduct, is = shader result, i = lamp energy (with shadow) */
/* tangent case; calculate fake face normal, aligned with lampvector */
if(vlr->flag & R_TANGENT) {
float cross[3];
Crossf(cross, lv, vn);
Crossf(vnor, cross, shi->vn);
vnor[0]= -vnor[0];vnor[1]= -vnor[1];vnor[2]= -vnor[2];
vn= vnor;
}
inp= vn[0]*lv[0] + vn[1]*lv[1] + vn[2]*lv[2];
/* phong threshold to prevent backfacing faces having artefacts on ray shadow (terminator problem) */
if((ma->mode & MA_RAYBIAS) && (lar->mode & LA_SHAD_RAY) && (vlr->flag & R_SMOOTH)) {
float thresh= vlr->ob->smoothresh;
if(inp>thresh)
phongcorr= (inp-thresh)/(inp*(1.0-thresh));
else
phongcorr= 0.0;
}
else phongcorr= 1.0;
/* diffuse shaders */
if(lar->mode & LA_NO_DIFF) {
is= 0.0; // skip shaders
}
else if(lar->type==LA_HEMI) {
is= 0.5*inp + 0.5;
}
else {
if(lar->type==LA_AREA) {
/* single sided */
if(lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2]>0.0) {
inp= area_lamp_energy(shi->co, vn, lar);
}
else inp= 0.0;
}
/* diffuse shaders (oren nayer gets inp from area light) */
if(ma->diff_shader==MA_DIFF_ORENNAYAR) is= OrenNayar_Diff_i(inp, vn, lv, view, ma->roughness);
else if(ma->diff_shader==MA_DIFF_TOON) is= Toon_Diff(vn, lv, view, ma->param[0], ma->param[1]);
else if(ma->diff_shader==MA_DIFF_MINNAERT) is= Minnaert_Diff(inp, vn, view, ma->darkness);
else if(ma->diff_shader==MA_DIFF_FRESNEL) is= Fresnel_Diff(vn, lv, view, ma->param[0], ma->param[1]);
else is= inp; // Lambert
}
i= is*phongcorr;
if(i>0.0) {
i*= lampdist*shi->refl;
}
vn= shi->vn; // bring back original vector, we use special specular shaders for tangent
/* shadow and spec, (lampdist==0 outside spot) */
if(lampdist> 0.0) {
if(i>0.0 && (R.r.mode & R_SHADOW)) {
if(ma->mode & MA_SHADOW) {
if(lar->type==LA_HEMI); // no shadow
else {
if(lar->shb) {
shadfac[3] = testshadowbuf(lar->shb, shi->co, shi->dxco, shi->dyco, inp);
}
else if(lar->mode & LA_SHAD_RAY) {
ray_shadow(shi, lar, shadfac);
}
/* warning, here it skips the loop */
if(lar->mode & LA_ONLYSHADOW) {
shadfac[3]= i*lar->energy*(1.0-shadfac[3]);
shr->diff[0] -= shadfac[3]*shi->r;
shr->diff[1] -= shadfac[3]*shi->g;
shr->diff[2] -= shadfac[3]*shi->b;
continue;
}
if(shadfac[3]==0.0) continue;
i*= shadfac[3];
}
}
}
if(R.r.mode & R_RAYTRACE) {
extern void ray_translucent(ShadeInput *shi, LampRen *lar, float *distfac, float *co);
float co[3], distfac;
ray_translucent(shi, lar, &distfac, co);
if(distfac<0.01f*G.rt) {
// printf("distfac %f\n", distfac);
distfac= 1.0f - distfac/(0.01f*G.rt);
shr->diff[0]+= distfac;
shr->diff[1]+= distfac;
shr->diff[2]+= distfac;
}
}
/* specularity */
if(shadfac[3]>0.0 && shi->spec!=0.0 && !(lar->mode & LA_NO_SPEC)) {
if(lar->type==LA_HEMI) {
/* hemi uses no spec shaders (yet) */
lv[0]+= view[0];
lv[1]+= view[1];
lv[2]+= view[2];
Normalise(lv);
t= vn[0]*lv[0]+vn[1]*lv[1]+vn[2]*lv[2];
if(lar->type==LA_HEMI) {
t= 0.5*t+0.5;
}
t= shadfac[3]*shi->spec*spec(t, shi->har);
shr->spec[0]+= t*(lacol[0] * shi->specr);
shr->spec[1]+= t*(lacol[1] * shi->specg);
shr->spec[2]+= t*(lacol[2] * shi->specb);
}
else {
/* specular shaders */
float specfac;
if(ma->spec_shader==MA_SPEC_PHONG)
specfac= Phong_Spec(vn, lv, view, shi->har, vlr->flag & R_TANGENT);
else if(ma->spec_shader==MA_SPEC_COOKTORR)
specfac= CookTorr_Spec(vn, lv, view, shi->har, vlr->flag & R_TANGENT);
else if(ma->spec_shader==MA_SPEC_BLINN)
specfac= Blinn_Spec(vn, lv, view, ma->refrac, (float)shi->har, vlr->flag & R_TANGENT);
else if(ma->spec_shader==MA_SPEC_WARDISO)
specfac= WardIso_Spec( vn, lv, view, ma->rms, vlr->flag & R_TANGENT);
else
specfac= Toon_Spec(vn, lv, view, ma->param[2], ma->param[3], vlr->flag & R_TANGENT);
/* area lamp correction */
if(lar->type==LA_AREA) specfac*= inp;
t= shadfac[3]*shi->spec*lampdist*specfac;
if(ma->mode & MA_RAMP_SPEC) {
float spec[3];
do_specular_ramp(shi, specfac, t, spec);
shr->spec[0]+= t*(lacol[0] * spec[0]);
shr->spec[1]+= t*(lacol[1] * spec[1]);
shr->spec[2]+= t*(lacol[2] * spec[2]);
}
else {
shr->spec[0]+= t*(lacol[0] * shi->specr);
shr->spec[1]+= t*(lacol[1] * shi->specg);
shr->spec[2]+= t*(lacol[2] * shi->specb);
}
}
}
}
/* in case 'no diffuse' we still do most calculus, spec can be in shadow */
if(i>0.0 && !(lar->mode & LA_NO_DIFF)) {
if(ma->mode & MA_SHADOW_TRA) {
add_to_diffuse(shr->diff, shi, is, i*shadfac[0]*lacol[0], i*shadfac[1]*lacol[1], i*shadfac[2]*lacol[2]);
}
else {
add_to_diffuse(shr->diff, shi, is, i*lacol[0], i*lacol[1], i*lacol[2]);
}
}
}
if(ma->mode & (MA_ZTRA|MA_RAYTRANSP)) {
if(ma->fresnel_tra!=0.0)
shi->alpha*= fresnel_fac(shi->view, shi->vn, ma->fresnel_tra_i, ma->fresnel_tra);
if(shi->spectra!=0.0) {
t = MAX3(shr->spec[0], shr->spec[1], shr->spec[2]);
t *= shi->spectra;
if(t>1.0) t= 1.0;
shi->alpha= (1.0-t)*shi->alpha+t;
}
}
shr->alpha= shi->alpha;
shr->diff[0]+= shi->r*shi->amb*shi->rad[0];
shr->diff[0]+= shi->ambr;
shr->diff[1]+= shi->g*shi->amb*shi->rad[1];
shr->diff[1]+= shi->ambg;
shr->diff[2]+= shi->b*shi->amb*shi->rad[2];
shr->diff[2]+= shi->ambb;
if(ma->mode & MA_RAMP_COL) ramp_diffuse_result(shr->diff, shi);
if(ma->mode & MA_RAMP_SPEC) ramp_spec_result(shr->spec, shr->spec+1, shr->spec+2, shi);
/* refcol is for envmap only */
if(shi->refcol[0]!=0.0) {
shr->diff[0]= shi->mirr*shi->refcol[1] + (1.0 - shi->mirr*shi->refcol[0])*shr->diff[0];
shr->diff[1]= shi->mirg*shi->refcol[2] + (1.0 - shi->mirg*shi->refcol[0])*shr->diff[1];
shr->diff[2]= shi->mirb*shi->refcol[3] + (1.0 - shi->mirb*shi->refcol[0])*shr->diff[2];
}
}
/* this function sets all coords for render (shared with raytracer) */
/* warning; exception for ortho render is here, can be done better! */
void shade_input_set_coords(ShadeInput *shi, float u, float v, int i1, int i2, int i3)
{
VertRen *v1, *v2, *v3;
VlakRen *vlr= shi->vlr;
float l, dl;
short texco= shi->mat->texco;
int mode= shi->mat->mode_l; /* or-ed result for all layers */
char p1, p2, p3;
/* for rendering of quads, the following values are used to denote vertices:
0 1 2 scanline tria & first half quad, and ray tria
0 2 3 scanline 2nd half quad
0 1 3 raytracer first half quad
2 1 3 raytracer 2nd half quad
*/
if(i1==0) {
v1= vlr->v1;
p1= ME_FLIPV1;
} else {
v1= vlr->v3;
p1= ME_FLIPV3;
}
if(i2==1) {
v2= vlr->v2;
p2= ME_FLIPV2;
} else {
v2= vlr->v3;
p2= ME_FLIPV3;
}
if(i3==2) {
v3= vlr->v3;
p3= ME_FLIPV3;
} else {
v3= vlr->v4;
p3= ME_FLIPV4;
}
/* calculate U and V, for scanline (normal u and v are -1 to 0) */
if(u==1.0) {
/* exception case for wire render of edge */
if(vlr->v2==vlr->v3);
else if( (vlr->flag & R_SMOOTH) || (texco & NEED_UV) ) {
float detsh, t00, t10, t01, t11;
if(vlr->snproj==0) {
t00= v3->co[0]-v1->co[0]; t01= v3->co[1]-v1->co[1];
t10= v3->co[0]-v2->co[0]; t11= v3->co[1]-v2->co[1];
}
else if(vlr->snproj==1) {
t00= v3->co[0]-v1->co[0]; t01= v3->co[2]-v1->co[2];
t10= v3->co[0]-v2->co[0]; t11= v3->co[2]-v2->co[2];
}
else {
t00= v3->co[1]-v1->co[1]; t01= v3->co[2]-v1->co[2];
t10= v3->co[1]-v2->co[1]; t11= v3->co[2]-v2->co[2];
}
detsh= 1.0/(t00*t11-t10*t01);
t00*= detsh; t01*=detsh;
t10*=detsh; t11*=detsh;
if(vlr->snproj==0) {
u= (shi->co[0]-v3->co[0])*t11-(shi->co[1]-v3->co[1])*t10;
v= (shi->co[1]-v3->co[1])*t00-(shi->co[0]-v3->co[0])*t01;
if(shi->osatex) {
shi->dxuv[0]= shi->dxco[0]*t11- shi->dxco[1]*t10;
shi->dxuv[1]= shi->dxco[1]*t00- shi->dxco[0]*t01;
shi->dyuv[0]= shi->dyco[0]*t11- shi->dyco[1]*t10;
shi->dyuv[1]= shi->dyco[1]*t00- shi->dyco[0]*t01;
}
}
else if(vlr->snproj==1) {
u= (shi->co[0]-v3->co[0])*t11-(shi->co[2]-v3->co[2])*t10;
v= (shi->co[2]-v3->co[2])*t00-(shi->co[0]-v3->co[0])*t01;
if(shi->osatex) {
shi->dxuv[0]= shi->dxco[0]*t11- shi->dxco[2]*t10;
shi->dxuv[1]= shi->dxco[2]*t00- shi->dxco[0]*t01;
shi->dyuv[0]= shi->dyco[0]*t11- shi->dyco[2]*t10;
shi->dyuv[1]= shi->dyco[2]*t00- shi->dyco[0]*t01;
}
}
else {
u= (shi->co[1]-v3->co[1])*t11-(shi->co[2]-v3->co[2])*t10;
v= (shi->co[2]-v3->co[2])*t00-(shi->co[1]-v3->co[1])*t01;
if(shi->osatex) {
shi->dxuv[0]= shi->dxco[1]*t11- shi->dxco[2]*t10;
shi->dxuv[1]= shi->dxco[2]*t00- shi->dxco[1]*t01;
shi->dyuv[0]= shi->dyco[1]*t11- shi->dyco[2]*t10;
shi->dyuv[1]= shi->dyco[2]*t00- shi->dyco[1]*t01;
}
}
}
}
l= 1.0+u+v;
/* calculate punos (vertexnormals) */
if(vlr->flag & R_SMOOTH) {
float n1[3], n2[3], n3[3];
if(shi->puno & p1) {
n1[0]= -v1->n[0]; n1[1]= -v1->n[1]; n1[2]= -v1->n[2];
} else {
n1[0]= v1->n[0]; n1[1]= v1->n[1]; n1[2]= v1->n[2];
}
if(shi->puno & p2) {
n2[0]= -v2->n[0]; n2[1]= -v2->n[1]; n2[2]= -v2->n[2];
} else {
n2[0]= v2->n[0]; n2[1]= v2->n[1]; n2[2]= v2->n[2];
}
if(shi->puno & p3) {
n3[0]= -v3->n[0]; n3[1]= -v3->n[1]; n3[2]= -v3->n[2];
} else {
n3[0]= v3->n[0]; n3[1]= v3->n[1]; n3[2]= v3->n[2];
}
shi->vn[0]= l*n3[0]-u*n1[0]-v*n2[0];
shi->vn[1]= l*n3[1]-u*n1[1]-v*n2[1];
shi->vn[2]= l*n3[2]-u*n1[2]-v*n2[2];
Normalise(shi->vn);
if(shi->osatex && (texco & (TEXCO_NORM|TEXCO_REFL)) ) {
dl= shi->dxuv[0]+shi->dxuv[1];
shi->dxno[0]= dl*n3[0]-shi->dxuv[0]*n1[0]-shi->dxuv[1]*n2[0];
shi->dxno[1]= dl*n3[1]-shi->dxuv[0]*n1[1]-shi->dxuv[1]*n2[1];
shi->dxno[2]= dl*n3[2]-shi->dxuv[0]*n1[2]-shi->dxuv[1]*n2[2];
dl= shi->dyuv[0]+shi->dyuv[1];
shi->dyno[0]= dl*n3[0]-shi->dyuv[0]*n1[0]-shi->dyuv[1]*n2[0];
shi->dyno[1]= dl*n3[1]-shi->dyuv[0]*n1[1]-shi->dyuv[1]*n2[1];
shi->dyno[2]= dl*n3[2]-shi->dyuv[0]*n1[2]-shi->dyuv[1]*n2[2];
}
}
else {
VECCOPY(shi->vn, shi->facenor);
}
/* texture coordinates. shi->dxuv shi->dyuv have been set */
if(texco & NEED_UV) {
if(texco & TEXCO_ORCO) {
if(v1->orco) {
float *o1, *o2, *o3;
o1= v1->orco;
o2= v2->orco;
o3= v3->orco;
shi->lo[0]= l*o3[0]-u*o1[0]-v*o2[0];
shi->lo[1]= l*o3[1]-u*o1[1]-v*o2[1];
shi->lo[2]= l*o3[2]-u*o1[2]-v*o2[2];
if(shi->osatex) {
dl= shi->dxuv[0]+shi->dxuv[1];
shi->dxlo[0]= dl*o3[0]-shi->dxuv[0]*o1[0]-shi->dxuv[1]*o2[0];
shi->dxlo[1]= dl*o3[1]-shi->dxuv[0]*o1[1]-shi->dxuv[1]*o2[1];
shi->dxlo[2]= dl*o3[2]-shi->dxuv[0]*o1[2]-shi->dxuv[1]*o2[2];
dl= shi->dyuv[0]+shi->dyuv[1];
shi->dylo[0]= dl*o3[0]-shi->dyuv[0]*o1[0]-shi->dyuv[1]*o2[0];
shi->dylo[1]= dl*o3[1]-shi->dyuv[0]*o1[1]-shi->dyuv[1]*o2[1];
shi->dylo[2]= dl*o3[2]-shi->dyuv[0]*o1[2]-shi->dyuv[1]*o2[2];
}
}
}
if(texco & TEXCO_GLOB) {
VECCOPY(shi->gl, shi->co);
MTC_Mat4MulVecfl(R.viewinv, shi->gl);
if(shi->osatex) {
VECCOPY(shi->dxgl, shi->dxco);
MTC_Mat3MulVecfl(R.imat, shi->dxco);
VECCOPY(shi->dygl, shi->dyco);
MTC_Mat3MulVecfl(R.imat, shi->dyco);
}
}
if(texco & TEXCO_STRAND) {
shi->strand= (l*v3->accum - u*v1->accum - v*v2->accum);
if(shi->osatex) {
dl= shi->dxuv[0]+shi->dxuv[1];
shi->dxstrand= dl*v3->accum-shi->dxuv[0]*v1->accum-shi->dxuv[1]*v2->accum;
dl= shi->dyuv[0]+shi->dyuv[1];
shi->dystrand= dl*v3->accum-shi->dyuv[0]*v1->accum-shi->dyuv[1]*v2->accum;
}
}
if((texco & TEXCO_UV) || (mode & (MA_VERTEXCOL|MA_VERTEXCOLP|MA_FACETEXTURE))) {
int j1=i1, j2=i2, j3=i3;
/* to prevent storing new tfaces or vcols, we check a split runtime */
/* 4---3 4---3 */
/* |\ 1| or |1 /| */
/* |0\ | |/ 0| */
/* 1---2 1---2 0 = orig face, 1 = new face */
/* Update vert nums to point to correct verts of original face */
if(vlr->flag & R_DIVIDE_24) {
if(vlr->flag & R_FACE_SPLIT) {
j1++; j2++; j3++;
}
else {
j3++;
}
}
else if(vlr->flag & R_FACE_SPLIT) {
j2++; j3++;
}
if(mode & (MA_VERTEXCOL|MA_VERTEXCOLP)) {
if(vlr->vcol) {
char *cp1, *cp2, *cp3;
cp1= (char *)(vlr->vcol+j1);
cp2= (char *)(vlr->vcol+j2);
cp3= (char *)(vlr->vcol+j3);
shi->vcol[0]= (l*cp3[3]-u*cp1[3]-v*cp2[3])/255.0;
shi->vcol[1]= (l*cp3[2]-u*cp1[2]-v*cp2[2])/255.0;
shi->vcol[2]= (l*cp3[1]-u*cp1[1]-v*cp2[1])/255.0;
}
else {
shi->vcol[0]= 0.0;
shi->vcol[1]= 0.0;
shi->vcol[2]= 0.0;
}
}
if(vlr->tface) {
float *uv1, *uv2, *uv3;
uv1= vlr->tface->uv[j1];
uv2= vlr->tface->uv[j2];
uv3= vlr->tface->uv[j3];
shi->uv[0]= -1.0 + 2.0*(l*uv3[0]-u*uv1[0]-v*uv2[0]);
shi->uv[1]= -1.0 + 2.0*(l*uv3[1]-u*uv1[1]-v*uv2[1]);
shi->uv[2]= 0.0; // texture.c assumes there are 3 coords
if(shi->osatex) {
float duv[2];
dl= shi->dxuv[0]+shi->dxuv[1];
duv[0]= shi->dxuv[0];
duv[1]= shi->dxuv[1];
shi->dxuv[0]= 2.0*(dl*uv3[0]-duv[0]*uv1[0]-duv[1]*uv2[0]);
shi->dxuv[1]= 2.0*(dl*uv3[1]-duv[0]*uv1[1]-duv[1]*uv2[1]);
dl= shi->dyuv[0]+shi->dyuv[1];
duv[0]= shi->dyuv[0];
duv[1]= shi->dyuv[1];
shi->dyuv[0]= 2.0*(dl*uv3[0]-duv[0]*uv1[0]-duv[1]*uv2[0]);
shi->dyuv[1]= 2.0*(dl*uv3[1]-duv[0]*uv1[1]-duv[1]*uv2[1]);
}
#if 0
{ /* tangent derived from UV, comes back later! (ton) */
//float s1= uv2[0] - uv1[0];
//float s2= uv3[0] - uv1[0];
float t1= uv2[1] - uv1[1];
float t2= uv3[1] - uv1[1];
shi->vn[0]= (t2 * (v2->co[0]-v1->co[0]) - t1 * (v3->co[0]-v1->co[0]));
shi->vn[1]= (t2 * (v2->co[1]-v1->co[1]) - t1 * (v3->co[1]-v1->co[1]));
shi->vn[2]= (t2 * (v2->co[2]-v1->co[2]) - t1 * (v3->co[2]-v1->co[2]));
Normalise(shi->vn);
vlr->flag |= R_TANGENT;
}
#endif
if(mode & MA_FACETEXTURE) {
if((mode & (MA_VERTEXCOL|MA_VERTEXCOLP))==0) {
shi->vcol[0]= 1.0;
shi->vcol[1]= 1.0;
shi->vcol[2]= 1.0;
}
if(vlr->tface) render_realtime_texture(shi);
}
}
else {
shi->uv[0]= 2.0*(u+.5);
shi->uv[1]= 2.0*(v+.5);
shi->uv[2]= 0.0; // texture.c assumes there are 3 coords
if(mode & MA_FACETEXTURE) {
/* no tface? set at 1.0 */
shi->vcol[0]= 1.0;
shi->vcol[1]= 1.0;
shi->vcol[2]= 1.0;
}
}
}
if(texco & TEXCO_NORM) {
shi->orn[0]= -shi->vn[0];
shi->orn[1]= -shi->vn[1];
shi->orn[2]= -shi->vn[2];
}
if(mode & MA_RADIO) {
shi->rad[0]= (l*v3->rad[0] - u*v1->rad[0] - v*v2->rad[0]);
shi->rad[1]= (l*v3->rad[1] - u*v1->rad[1] - v*v2->rad[1]);
shi->rad[2]= (l*v3->rad[2] - u*v1->rad[2] - v*v2->rad[2]);
}
else {
shi->rad[0]= shi->rad[1]= shi->rad[2]= 0.0;
}
if(texco & TEXCO_REFL) {
/* mirror reflection colour textures (and envmap) */
calc_R_ref(shi);
}
}
else {
shi->rad[0]= shi->rad[1]= shi->rad[2]= 0.0;
}
}
#if 0
/* return labda for view vector being closest to line v3-v4 */
/* was used for wire render */
static float isec_view_line(float *view, float *v3, float *v4)
{
float vec[3];
float dot0, dot1, dot2, veclen, viewlen;
float fac, div;
vec[0]= v4[0] - v3[0];
vec[1]= v4[1] - v3[1];
vec[2]= v4[2] - v3[2];
dot0 = v3[0]*vec[0] + v3[1]*vec[1] + v3[2]*vec[2];
dot1 = vec[0]*view[0] + vec[1]*view[1] + vec[2]*view[2];
dot2 = v3[0]*view[0] + v3[1]*view[1] + v3[2]*view[2];
veclen = vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2];
viewlen = view[0]*view[0] + view[1]*view[1] + view[2]*view[2];
div = viewlen*veclen - dot1*dot1;
if (div==0.0) return 0.0;
fac = dot2*veclen - dot0*dot1;
return fac/div;
}
#endif
void matlayer_blend(MaterialLayer *ml, float blendfac, ShadeResult *target, ShadeResult *src)
{
if(ml->flag & ML_DIFFUSE)
ramp_blend(ml->blendmethod, target->diff, target->diff+1, target->diff+2, blendfac*src->alpha, src->diff);
if(ml->flag & ML_SPECULAR)
ramp_blend(ml->blendmethod, target->spec, target->spec+1, target->spec+2, blendfac*src->alpha, src->spec);
if(ml->flag & ML_ALPHA)
ramp_blend(ml->blendmethod, &target->alpha, NULL, NULL, blendfac, &src->alpha);
}
/* x,y: window coordinate from 0 to rectx,y */
/* return pointer to rendered face */
void *shadepixel(float x, float y, int z, int facenr, int mask, float *col, float *rco)
{
ShadeResult shr;
ShadeInput shi;
VlakRen *vlr=NULL;
if(facenr< 0) { /* error */
return NULL;
}
/* currently in use for dithering (soft shadow) and detecting thread */
shi.xs= x;
shi.ys= y;
/* mask is used to indicate amount of samples (ray shad/mir and AO) */
shi.mask= mask;
shi.depth= 0; // means first hit, not raytracing
if(facenr==0) { /* sky */
col[0]= 0.0; col[1]= 0.0; col[2]= 0.0; col[3]= 0.0;
VECCOPY(rco, col);
}
else if( (facenr & 0x7FFFFF) <= R.totvlak) {
VertRen *v1, *v2, *v3;
Material *mat;
MaterialLayer *ml;
float alpha, fac, zcor;
vlr= RE_findOrAddVlak( (facenr-1) & 0x7FFFFF);
shi.vlr= vlr;
mat= shi.mat= vlr->mat;
shi.osatex= (shi.mat->texco & TEXCO_OSA);
/* copy the face normal (needed because it gets flipped for tracing */
VECCOPY(shi.facenor, vlr->n);
shi.puno= vlr->puno;
v1= vlr->v1;
/* COXYZ AND VIEW VECTOR */
calc_view_vector(shi.view, x, y);
/* wire cannot use normal for calculating shi.co */
if(shi.mat->mode & MA_WIRE) {
float zco;
/* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */
zco= ((float)z)/(float)0x7FFFFFFF;
shi.co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
fac= zcor= shi.co[2]/shi.view[2];
shi.co[0]= fac*shi.view[0];
shi.co[1]= fac*shi.view[1];
}
else {
float dface;
dface= v1->co[0]*shi.facenor[0]+v1->co[1]*shi.facenor[1]+v1->co[2]*shi.facenor[2];
/* ortho viewplane cannot intersect using view vector originating in (0,0,0) */
if(R.r.mode & R_ORTHO) {
/* x and y 3d coordinate can be derived from pixel coord and winmat */
float fx= 2.0/(R.rectx*R.winmat[0][0]);
float fy= 2.0/(R.recty*R.winmat[1][1]);
shi.co[0]= (0.5 + x - 0.5*R.rectx)*fx - R.winmat[3][0]/R.winmat[0][0];
shi.co[1]= (0.5 + y - 0.5*R.recty)*fy - R.winmat[3][1]/R.winmat[1][1];
/* using a*x + b*y + c*z = d equation, (a b c) is normal */
shi.co[2]= (dface - shi.facenor[0]*shi.co[0] - shi.facenor[1]*shi.co[1])/shi.facenor[2];
zcor= 1.0; // only to prevent not-initialize
if(shi.osatex || (R.r.mode & R_SHADOW) ) {
shi.dxco[0]= fx;
shi.dxco[1]= 0.0;
shi.dxco[2]= (shi.facenor[0]*fx)/shi.facenor[2];
shi.dyco[0]= 0.0;
shi.dyco[1]= fy;
shi.dyco[2]= (shi.facenor[1]*fy)/shi.facenor[2];
}
}
else {
float div;
div= shi.facenor[0]*shi.view[0] + shi.facenor[1]*shi.view[1] + shi.facenor[2]*shi.view[2];
if (div!=0.0) fac= zcor= dface/div;
else fac= zcor= 0.0;
shi.co[0]= fac*shi.view[0];
shi.co[1]= fac*shi.view[1];
shi.co[2]= fac*shi.view[2];
/* pixel dx/dy for render coord */
if(shi.osatex || (R.r.mode & R_SHADOW) ) {
float u= dface/(div-shi.facenor[0]);
float v= dface/(div- R.ycor*shi.facenor[1]);
shi.dxco[0]= shi.co[0]- (shi.view[0]-1.0)*u;
shi.dxco[1]= shi.co[1]- (shi.view[1])*u;
shi.dxco[2]= shi.co[2]- (shi.view[2])*u;
shi.dyco[0]= shi.co[0]- (shi.view[0])*v;
shi.dyco[1]= shi.co[1]- (shi.view[1]-1.0*R.ycor)*v;
shi.dyco[2]= shi.co[2]- (shi.view[2])*v;
}
}
}
/* rco might be used for sky texture */
VECCOPY(rco, shi.co);
/* cannot normalise earlier, code above needs it at pixel level */
fac= Normalise(shi.view);
zcor*= fac; // for mist, distance of point from camera
if(shi.osatex) {
if( (shi.mat->texco & TEXCO_REFL) ) {
shi.dxview= -1.0/fac;
shi.dyview= -R.ycor/fac;
}
}
/* calcuate normals, texture coords, vertex colors, etc */
if(facenr & 0x800000)
shade_input_set_coords(&shi, 1.0, 1.0, 0, 2, 3);
else
shade_input_set_coords(&shi, 1.0, 1.0, 0, 1, 2);
/* this only avalailable for scanline */
if(shi.mat->texco & TEXCO_WINDOW) {
shi.winco[0]= (x+(R.xstart))/(float)R.afmx;
shi.winco[1]= (y+(R.ystart))/(float)R.afmy;
shi.winco[2]= 0.0;
if(shi.osatex) {
shi.dxwin[0]= 0.5/(float)R.r.xsch;
shi.dywin[1]= 0.5/(float)R.r.ysch;
shi.dxwin[1]= shi.dxwin[2]= 0.0;
shi.dywin[0]= shi.dywin[2]= 0.0;
}
}
/* after this the u and v AND shi.dxuv and shi.dyuv are incorrect */
if(shi.mat->texco & TEXCO_STICKY) {
if(v1->sticky) {
extern float Zmulx, Zmuly;
float *o1, *o2, *o3, hox, hoy, l, dl, u, v;
float s00, s01, s10, s11, detsh;
if(facenr & 0x800000) {
v2= vlr->v3; v3= vlr->v4;
} else {
v2= vlr->v2; v3= vlr->v3;
}
s00= v3->ho[0]/v3->ho[3] - v1->ho[0]/v1->ho[3];
s01= v3->ho[1]/v3->ho[3] - v1->ho[1]/v1->ho[3];
s10= v3->ho[0]/v3->ho[3] - v2->ho[0]/v2->ho[3];
s11= v3->ho[1]/v3->ho[3] - v2->ho[1]/v2->ho[3];
detsh= s00*s11-s10*s01;
s00/= detsh; s01/=detsh;
s10/=detsh; s11/=detsh;
/* recalc u and v again */
hox= x/Zmulx -1.0;
hoy= y/Zmuly -1.0;
u= (hox - v3->ho[0]/v3->ho[3])*s11 - (hoy - v3->ho[1]/v3->ho[3])*s10;
v= (hoy - v3->ho[1]/v3->ho[3])*s00 - (hox - v3->ho[0]/v3->ho[3])*s01;
l= 1.0+u+v;
o1= v1->sticky;
o2= v2->sticky;
o3= v3->sticky;
shi.sticky[0]= l*o3[0]-u*o1[0]-v*o2[0];
shi.sticky[1]= l*o3[1]-u*o1[1]-v*o2[1];
shi.sticky[2]= 0.0;
if(shi.osatex) {
shi.dxuv[0]= s11/Zmulx;
shi.dxuv[1]= - s01/Zmulx;
shi.dyuv[0]= - s10/Zmuly;
shi.dyuv[1]= s00/Zmuly;
dl= shi.dxuv[0]+shi.dxuv[1];
shi.dxsticky[0]= dl*o3[0]-shi.dxuv[0]*o1[0]-shi.dxuv[1]*o2[0];
shi.dxsticky[1]= dl*o3[1]-shi.dxuv[0]*o1[1]-shi.dxuv[1]*o2[1];
dl= shi.dyuv[0]+shi.dyuv[1];
shi.dysticky[0]= dl*o3[0]-shi.dyuv[0]*o1[0]-shi.dyuv[1]*o2[0];
shi.dysticky[1]= dl*o3[1]-shi.dyuv[0]*o1[1]-shi.dyuv[1]*o2[1];
}
}
}
/* ------ main shading loop -------- */
VECCOPY(shi.vno, shi.vn);
if(shi.mat->ml_flag & ML_RENDER) {
shade_lamp_loop(&shi, &shr); /* clears shr */
if(shi.translucency!=0.0) {
ShadeResult shr_t;
VECCOPY(shi.vn, shi.vno);
VecMulf(shi.vn, -1.0);
VecMulf(shi.facenor, -1.0);
shade_lamp_loop(&shi, &shr_t);
shr.diff[0]+= shi.translucency*shr_t.diff[0];
shr.diff[1]+= shi.translucency*shr_t.diff[1];
shr.diff[2]+= shi.translucency*shr_t.diff[2];
VecMulf(shi.vn, -1.0);
VecMulf(shi.facenor, -1.0);
}
if(R.r.mode & R_RAYTRACE) {
if(shi.ray_mirror!=0.0 || ((shi.mat->mode & MA_RAYTRANSP) && shr.alpha!=1.0)) {
ray_trace(&shi, &shr);
}
}
else {
/* doesnt look 'correct', but is better for preview, plus envmaps dont raytrace this */
if(shi.mat->mode & MA_RAYTRANSP) shr.alpha= 1.0;
}
}
else {
memset(&shr, 0, sizeof(ShadeResult));
shr.alpha= 1.0f;
}
for(ml= shi.mat->layers.first; ml; ml= ml->next) {
if(ml->mat && (ml->flag & ML_RENDER)) {
ShadeResult shrlay;
shi.mat= ml->mat;
shi.layerfac= ml->blendfac;
VECCOPY(shi.vn, shi.vno);
if(ml->flag & ML_NEG_NORMAL)
VecMulf(shi.vn, -1.0);
shade_lamp_loop(&shi, &shrlay); /* clears shrlay */
if(R.r.mode & R_RAYTRACE) {
if(shi.ray_mirror!=0.0 || ((shi.mat->mode & MA_RAYTRANSP) && shr.alpha!=1.0)) {
ray_trace(&shi, &shr);
}
}
else {
/* doesnt look 'correct', but is better for preview, plus envmaps dont raytrace this */
if(shi.mat->mode & MA_RAYTRANSP) shr.alpha= 1.0;
}
matlayer_blend(ml, shi.layerfac, &shr, &shrlay);
}
}
shi.mat= mat;
/* after shading and composit layers */
if(shr.spec[0]<0.0f) shr.spec[0]= 0.0f;
if(shr.spec[1]<0.0f) shr.spec[1]= 0.0f;
if(shr.spec[2]<0.0f) shr.spec[2]= 0.0f;
if(shr.diff[0]<0.0f) shr.diff[0]= 0.0f;
if(shr.diff[1]<0.0f) shr.diff[1]= 0.0f;
if(shr.diff[2]<0.0f) shr.diff[2]= 0.0f;
VECADD(col, shr.diff, shr.spec);
/* exposure correction */
if(R.wrld.exp!=0.0 || R.wrld.range!=1.0) {
if((shi.mat->mode & MA_SHLESS)==0) {
col[0]= R.wrld.linfac*(1.0-exp( col[0]*R.wrld.logfac) );
col[1]= R.wrld.linfac*(1.0-exp( col[1]*R.wrld.logfac) );
col[2]= R.wrld.linfac*(1.0-exp( col[2]*R.wrld.logfac) );
}
}
/* MIST */
if((R.wrld.mode & WO_MIST) && (shi.mat->mode & MA_NOMIST)==0 ) {
if(R.r.mode & R_ORTHO)
alpha= mistfactor(-shi.co[2], shi.co);
else
alpha= mistfactor(zcor, shi.co);
}
else alpha= 1.0;
if(shr.alpha!=1.0 || alpha!=1.0) {
if(shi.mat->mode & MA_RAYTRANSP) {
// sky was applied allready for ray transp, only do mist
col[3]= shr.alpha;
fac= alpha;
}
else {
fac= alpha*(shr.alpha);
col[3]= fac;
}
col[0]*= fac;
col[1]*= fac;
col[2]*= fac;
}
else col[3]= 1.0;
}
if(R.flag & R_LAMPHALO) {
if(facenr<=0) { /* calc view vector and put shi.co at far */
if(R.r.mode & R_ORTHO) {
/* x and y 3d coordinate can be derived from pixel coord and winmat */
float fx= 2.0/(R.rectx*R.winmat[0][0]);
float fy= 2.0/(R.recty*R.winmat[1][1]);
shi.co[0]= (0.5 + x - 0.5*R.rectx)*fx - R.winmat[3][0]/R.winmat[0][0];
shi.co[1]= (0.5 + y - 0.5*R.recty)*fy - R.winmat[3][1]/R.winmat[1][1];
}
calc_view_vector(shi.view, x, y);
shi.co[2]= 0.0;
renderspothalo(&shi, col, 1.0);
}
else
renderspothalo(&shi, col, col[3]);
}
return vlr;
}
static void shadepixel_sky(float x, float y, int z, int facenr, int mask, float *colf)
{
VlakRen *vlr;
float collector[4], rco[3];
vlr= shadepixel(x, y, z, facenr, mask, colf, rco);
if(colf[3] != 1.0) {
/* bail out when raytrace transparency (sky included already) */
if(vlr && (R.r.mode & R_RAYTRACE))
if(vlr->mat->mode & MA_RAYTRANSP) return;
renderSkyPixelFloat(collector, x, y, vlr?rco:NULL);
addAlphaOverFloat(collector, colf);
QUATCOPY(colf, collector);
}
}
/* ************* pixel struct ******** */
static PixStrMain psmfirst;
static int psmteller;
static PixStr *addpsmain(void)
{
PixStrMain *psm;
psm= &psmfirst;
while(psm->next) {
psm= psm->next;
}
psm->next= (PixStrMain *)MEM_mallocN(sizeof(PixStrMain),"pixstrMain");
psm= psm->next;
psm->next=0;
psm->ps= (PixStr *)MEM_mallocN(4096*sizeof(PixStr),"pixstr");
psmteller= 0;
return psm->ps;
}
static void freeps(void)
{
PixStrMain *psm,*next;
psm= &psmfirst;
while(psm) {
next= psm->next;
if(psm->ps) {
MEM_freeN(psm->ps);
psm->ps= 0;
}
if(psm!= &psmfirst) MEM_freeN(psm);
psm= next;
}
psmfirst.next= 0;
psmfirst.ps= 0;
}
static void addps(long *rd, int facenr, int z, unsigned short mask)
{
static PixStr *cur;
PixStr *ps, *last = NULL;
if(*rd) {
ps= (PixStr *)(*rd);
while(ps) {
if( ps->facenr == facenr ) {
ps->mask |= mask;
return;
}
last= ps;
ps= ps->next;
}
}
/* make new PS (pixel struct) */
if((psmteller & 4095)==0) cur= addpsmain();
else cur++;
psmteller++;
if(last) last->next= cur;
else *rd= (long)cur;
cur->next= NULL;
cur->facenr= facenr;
cur->z= z;
cur->mask = mask;
}
int count_mask(unsigned short mask)
{
extern char cmask[256];
return (cmask[mask & 255]+cmask[mask>>8]);
}
static void edge_enhance(void)
{
/* use zbuffer to define edges, add it to the image */
int val, y, x, col, *rz, *rz1, *rz2, *rz3;
int zval1, zval2, zval3;
char *cp;
/* shift values in zbuffer 4 to the right, for filter we need multiplying with 12 max */
rz= (int *)R.rectz;
if(rz==NULL) return;
for(y=0; y<R.recty; y++) {
for(x=0; x<R.rectx; x++, rz++) (*rz)>>= 4;
}
rz1= (int *)R.rectz;
rz2= rz1+R.rectx;
rz3= rz2+R.rectx;
if(R.r.mode & R_OSA) {
cp= (char *)(R.rectaccu+R.rectx);
}
else {
cp= (char *)(R.rectot+R.rectx);
}
cp+= 4;
for(y=0; y<R.recty-2; y++) {
for(x=0; x<R.rectx-2; x++, rz++, rz1++, rz2++, rz3++, cp+=4) {
/* 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 );
col >>= 5;
if(col > (1<<16)) col= (1<<16);
else col= (R.r.edgeint*col)>>8;
if(col>0) {
if(col>255) col= 255;
if(R.r.mode & R_OSA) {
col/= R.osa;
val= cp[3]+col;
if(val>255) cp[3]= 255; else cp[3]= val;
}
else {
val= cp[0]- col;
if(val<0) cp[0]= 0; else cp[0]= val;
val= cp[1]- col;
if(val<0) cp[1]= 0; else cp[1]= val;
val= cp[2]- col;
if(val<0) cp[2]= 0; else cp[2]= val;
}
}
}
rz++;
rz1+= 2;
rz2+= 2;
rz3+= 2;
cp+= 8;
}
}
/* ********************* MAINLOOPS ******************** */
struct renderlineDA {
long *rd;
int *rz;
float *rb1, *rb2, *rb3;
float *acol;
int y;
};
static int do_renderlineDA(void *poin)
{
struct renderlineDA *rl= poin;
PixStr *ps;
float xs, ys;
float fcol[4], *acol=NULL, *rb1, *rb2, *rb3;
long *rd= rl->rd;
int zbuf, samp, curmask, face, mask, fullmask;
int b, x, full_osa, seed;
/* we set per pixel a fixed seed, for random AO and shadow samples */
seed= (R.ystart + rl->y + R.afmy)*R.r.xsch + R.xstart + R.afmx;
fullmask= (1<<R.osa)-1;
rb1= rl->rb1;
rb2= rl->rb2;
rb3= rl->rb3;
if(R.flag & R_ZTRA) { /* zbuf tra */
abufsetrow(rl->acol, rl->y);
acol= rl->acol;
}
for(x=0; x<R.rectx; x++, rd++) {
BLI_thread_srandom(rl->y & 1, seed+x);
ps= (PixStr *)(*rd);
mask= 0;
/* complex loop, because empty spots are sky, without mask */
while(TRUE) {
if(ps==NULL) {
face= 0;
curmask= (~mask) & fullmask;
zbuf= *(rl->rz+x);
}
else {
face= ps->facenr;
curmask= ps->mask;
zbuf= ps->z;
}
/* check osa level */
if(face==0) full_osa= 0;
else {
VlakRen *vlr= RE_findOrAddVlak( (face-1) & 0x7FFFFF);
full_osa= (vlr->flag & R_FULL_OSA);
}
if(full_osa) {
for(samp=0; samp<R.osa; samp++) {
if(curmask & (1<<samp)) {
xs= (float)x + jit[samp][0];
ys= (float)rl->y + jit[samp][1];
shadepixel_sky(xs, ys, zbuf, face, (1<<samp), fcol);
if(acol && acol[3]!=0.0) addAlphaOverFloat(fcol, acol);
if(do_gamma) {
fcol[0]= gammaCorrect(fcol[0]);
fcol[1]= gammaCorrect(fcol[1]);
fcol[2]= gammaCorrect(fcol[2]);
}
add_filt_fmask(1<<samp, fcol, rb1, rb2, rb3);
}
}
}
else {
extern char *centmask; // initrender.c
extern float centLut[16];
b= centmask[curmask];
xs= (float)x+centLut[b & 15];
ys= (float)rl->y+centLut[b>>4];
shadepixel_sky(xs, ys, zbuf, face, curmask, fcol);
if(acol && acol[3]!=0.0) addAlphaOverFloat(fcol, acol);
if(do_gamma) {
fcol[0]= gammaCorrect(fcol[0]);
fcol[1]= gammaCorrect(fcol[1]);
fcol[2]= gammaCorrect(fcol[2]);
}
add_filt_fmask(curmask, fcol, rb1, rb2, rb3);
}
mask |= curmask;
if(ps==NULL) break;
else ps= ps->next;
}
rb1+=4;
rb2+=4;
rb3+=4;
if(acol) acol+=4;
}
return 1;
}
void zbufshadeDA(void) /* Delta Accum Pixel Struct */
{
extern float Zjitx,Zjity;
struct renderlineDA rl1, rl2;
float xd, yd, *rf;
long *rd;
int *rz, *rp, *rt;
float *rowbuf1, *rowbuf2, *rowbuf3, *rowbuf0, *rowbuf1a, *rowbuf2a, *rb3;
int a;
short v, x, y;
R.rectdaps= MEM_callocN(sizeof(long)*R.rectx*R.recty+4,"zbufDArectd");
if(R.flag & R_ZTRA) {
bgnaccumbuf();
rl1.acol= MEM_callocN((R.rectx+4)*4*sizeof(float), "Acol");
rl2.acol= MEM_callocN((R.rectx+4)*4*sizeof(float), "Acol");
}
psmteller= 0;
if(R.r.mode & R_EDGE) {
R.rectaccu= (int *)MEM_callocN(sizeof(int)*R.rectx*R.recty,"zbufshadeDA");
}
for(v=0; v<R.osa; v++) {
xd= jit[v][0];
yd= jit[v][1];
Zjitx= -xd -0.5;
Zjity= -yd -0.5;
if((R.r.mode & R_MBLUR)==0) RE_local_printrenderinfo(0.0, v);
/* RECTDELTA */
fillrect(R.rectot,R.rectx,R.recty,0);
zbufferall();
rd= R.rectdaps;
rp= R.rectot;
rz= R.rectz;
for(y=0; y<R.recty; y++) {
for(x=0; x<R.rectx; x++, rp++, rd++) {
if(*rp) {
addps(rd, *rp, *(rz+x), 1<<v);
}
}
rz+= R.rectx;
}
if(R.r.mode & R_EDGE) edge_enhance();
if(RE_local_test_break()) break;
}
rd= R.rectdaps;
rz= R.rectz;
rt= R.rectot;
rf= R.rectftot;
/* the rowbuf is 4 pixels larger than an image! */
rowbuf0= MEM_callocN((R.rectx+4)*4*sizeof(float), "ZbufshadeDA3");
rowbuf1= MEM_callocN((R.rectx+4)*4*sizeof(float), "ZbufshadeDA3");
rowbuf2= MEM_callocN((R.rectx+4)*4*sizeof(float), "ZbufshadeDA3");
rowbuf1a= MEM_callocN((R.rectx+4)*4*sizeof(float), "ZbufshadeDA3");
rowbuf2a= MEM_callocN((R.rectx+4)*4*sizeof(float), "ZbufshadeDA3");
rowbuf3= MEM_callocN((R.rectx+4)*4*sizeof(float), "ZbufshadeDA3");
for(y=0; y<=R.recty; y++, rd+=R.rectx, rt+=R.rectx, rz+= R.rectx) {
if(y<R.recty) {
rl1.rd= rd;
rl1.rz= rz;
rl1.y= y;
rl1.rb1= rowbuf1;
rl1.rb2= rowbuf2;
rl1.rb3= rowbuf3;
if( (R.r.mode & R_THREADS) && y!=R.recty-1) { // odd amount of total y pixels...
if((y & 1)==0) {
SDL_Thread *thread;
thread = SDL_CreateThread(do_renderlineDA, &rl1);
if ( thread == NULL ) {
fprintf(stderr, "Unable to create thread");
G.afbreek= 1;
break;
}
rl2.rd= rd+R.rectx;
rl2.rz= rz+R.rectx;
rl2.y= y+1;
rl2.rb1= rowbuf0;
rl2.rb2= rowbuf1a;
rl2.rb3= rowbuf2a;
do_renderlineDA(&rl2);
SDL_WaitThread(thread, NULL);
if(R.r.filtertype) {
float *rb1= rowbuf1, *rb2= rowbuf2, *rb1a= rowbuf1a, *rb2a= rowbuf2a;
a= 4*(R.rectx + 4);
while(a--) {
*rb1 += *rb1a;
*rb2 += *rb2a;
*(rb1a++)= 0; rb1++;
*(rb2a++)= 0; rb2++;
}
}
else {
SWAP(float *, rowbuf1a, rowbuf1);
}
}
}
else do_renderlineDA(&rl1);
}
if(y>0) {
/* halos are alpha-added, not in thread loop (yet) because of gauss mask */
if(R.flag & R_HALO) {
/* one scanline older... */
scanlinehaloPS(rz-R.rectx, rd-R.rectx, rowbuf3+4, y-1);
}
/* convert 4x32 bits buffer to 4x8, this can't be threaded due to gauss */
transferColourBufferToOutput(rowbuf3+4, y-1);
if(R.rectftot) {
memcpy(rf, rowbuf3+4, 4*sizeof(float)*R.rectx);
rf+= 4*R.rectx;
}
}
if(y<R.recty) {
memset(rowbuf3, 0, (R.rectx+4)*4*sizeof(int));
rb3= rowbuf3;
rowbuf3= rowbuf2;
rowbuf2= rowbuf1;
rowbuf1= rowbuf0;
rowbuf0= rb3;
if( y>0) {
if((y & 1)==0) {
RE_local_render_display(y-2, y-1, R.rectx, R.recty, R.rectot);
}
}
}
if(RE_local_test_break()) break;
}
if( (R.r.mode & R_EDGE) && RE_local_test_break()==0) {
if(R.rectftot) {
float *rtf= R.rectftot, colf[4];
rp= R.rectaccu;
for(a= R.rectx*R.recty; a>0; a--, rtf+=4, rp++) {
cpCharColV2FloatColV((char *)rp, colf);
addAlphaOverFloat(rtf, colf);
}
RE_floatbuffer_to_output();
}
else {
rt= R.rectot;
rp= R.rectaccu;
for(a= R.rectx*R.recty; a>0; a--, rt++, rp++) {
addalphaOver((char *)rt, (char *)rp);
}
}
}
MEM_freeN(R.rectdaps);
freeps();
MEM_freeN(rowbuf0);
MEM_freeN(rowbuf1);
MEM_freeN(rowbuf2);
MEM_freeN(rowbuf1a);
MEM_freeN(rowbuf2a);
MEM_freeN(rowbuf3);
R.rectdaps= NULL;
if(R.r.mode & R_EDGE) if(R.rectaccu) MEM_freeN(R.rectaccu);
R.rectaccu= NULL;
if(R.flag & R_ZTRA) {
endaccumbuf();
MEM_freeN(rl1.acol);
MEM_freeN(rl2.acol);
}
} /* end of void zbufshadeDA() */
/* ------------------------------------------------------------------------ */
struct renderline {
float *rowbuf, *acol;
int *rp;
int *rz;
short ys;
float y;
};
static int do_renderline(void *poin)
{
struct renderline *rl= poin;
float *fcol= rl->rowbuf;
float *acol=NULL;
int x, *rz, *rp, seed;
/* we set per pixel a fixed seed, for random AO and shadow samples */
seed= (R.ystart + rl->y + R.afmy)*R.r.xsch + R.xstart + R.afmx;
if(R.flag & R_ZTRA) { /* zbuf tra */
abufsetrow(rl->acol, rl->ys);
acol= rl->acol;
}
for(x=0, rz= rl->rz, rp= rl->rp; x<R.rectx; x++, rz++, rp++, fcol+=4) {
BLI_thread_srandom(rl->ys & 1, seed+x);
shadepixel_sky((float)x, rl->y, *rz, *rp, 0, fcol);
if(acol) {
if(acol[3]!=0.0) addAlphaOverFloat(fcol, acol);
acol+= 4;
}
}
if(R.flag & R_HALO) {
scanlinehalo(rl->rz, rl->rowbuf, rl->ys);
}
transferColourBufferToOutput(rl->rowbuf, rl->ys);
if(R.rectftot) {
memcpy(R.rectftot + 4*rl->ys*R.rectx, rl->rowbuf, 4*sizeof(float)*R.rectx);
}
return 1;
}
void zbufshade(void)
{
struct renderline rl1, rl2;
extern float Zjitx,Zjity;
int *rz, *rp;
float fy;
int y;
rl1.rowbuf= MEM_callocN((R.rectx+4)*4*sizeof(float), "Zbufshade");
rl2.rowbuf= MEM_callocN((R.rectx+4)*4*sizeof(float), "Zbufshade");
Zjitx=Zjity= -0.5;
zbufferall();
/* SHADE */
rp= R.rectot;
rz= R.rectz;
if(R.flag & R_ZTRA) {
rl1.acol= MEM_callocN((R.rectx+4)*4*sizeof(float), "Acol");
rl2.acol= MEM_callocN((R.rectx+4)*4*sizeof(float), "Acol");
bgnaccumbuf();
}
for(y=0; y<R.recty; y++) {
fy= y;
rl1.rp= rp;
rl1.rz= rz;
rl1.y= fy;
rl1.ys= y;
if(R.r.mode & R_THREADS) {
SDL_Thread *thread;
thread = SDL_CreateThread(do_renderline, &rl1);
if ( thread == NULL ) {
fprintf(stderr, "Unable to create thread");
G.afbreek= 1;
break;
}
rp+= R.rectx;
rz+= R.rectx;
if(y < R.recty-1) {
rl2.rp= rp;
rl2.rz= rz;
rl2.y= fy+1.0;
rl2.ys= y+1;
do_renderline(&rl2);
rp+= R.rectx;
rz+= R.rectx;
y++;
}
SDL_WaitThread(thread, NULL);
}
else {
do_renderline(&rl1);
rp+= R.rectx;
rz+= R.rectx;
}
if(y & 1) {
RE_local_render_display(y-1, y, R.rectx, R.recty, R.rectot);
}
if(RE_local_test_break()) break;
}
MEM_freeN(rl1.rowbuf);
MEM_freeN(rl2.rowbuf);
if(R.flag & R_ZTRA) {
endaccumbuf();
MEM_freeN(rl1.acol);
MEM_freeN(rl2.acol);
}
if(R.r.mode & R_EDGE) edge_enhance();
} /* end of void zbufshade() */
/* ------------------------------------------------------------------------ */
void RE_shadehalo(HaloRen *har, char *col, float *colf, int zz, float dist, float xn, float yn, short flarec)
{
shadeHaloFloat(har, colf, zz, dist, xn, yn, flarec);
if(colf[0]<=0.0) col[0]= 0; else if(colf[0]>=1.0) col[0]= 255; else col[0]= 255.0*colf[0];
if(colf[1]<=0.0) col[1]= 0; else if(colf[1]>=1.0) col[1]= 255; else col[1]= 255.0*colf[1];
if(colf[2]<=0.0) col[2]= 0; else if(colf[2]>=1.0) col[2]= 255; else col[2]= 255.0*colf[2];
if(colf[3]<=0.0) col[3]= 0; else if(colf[3]>=1.0) col[3]= 255; else col[3]= 255.0*colf[3];
}
static void renderhalo(HaloRen *har) /* postprocess version */
{
float dist, xsq, ysq, xn, yn, colf[4], *rectft, *rtf;
int *rectt, *rt;
int minx, maxx, miny, maxy, x, y;
char col[4];
har->miny= miny= har->ys - har->rad/R.ycor;
har->maxy= maxy= har->ys + har->rad/R.ycor;
if(maxy<0);
else if(R.recty<miny);
else {
minx= floor(har->xs-har->rad);
maxx= ceil(har->xs+har->rad);
if(maxx<0);
else if(R.rectx<minx);
else {
if(minx<0) minx= 0;
if(maxx>=R.rectx) maxx= R.rectx-1;
if(miny<0) miny= 0;
if(maxy>R.recty) maxy= R.recty;
rectt= R.rectot+ R.rectx*miny;
rectft= R.rectftot+ 4*R.rectx*miny;
for(y=miny; y<maxy; y++) {
rt= rectt+minx;
rtf= rectft+4*minx;
yn= (y - har->ys)*R.ycor;
ysq= yn*yn;
for(x=minx; x<=maxx; x++) {
xn= x - har->xs;
xsq= xn*xn;
dist= xsq+ysq;
if(dist<har->radsq) {
shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec);
if(R.rectftot) addalphaAddfacFloat(rtf, colf, har->add);
else {
std_floatcol_to_charcol(colf, col);
RE_addalphaAddfac((char *)rt, col, har->add);
}
}
rt++;
rtf+=4;
}
rectt+= R.rectx;
rectft+= 4*R.rectx;
if(RE_local_test_break()) break;
}
}
}
}
/* ------------------------------------------------------------------------ */
void RE_renderflare(HaloRen *har)
{
extern float hashvectf[];
HaloRen fla;
Material *ma;
float *rc, rad, alfa, visifac, vec[3];
int b, type;
fla= *har;
fla.linec= fla.ringc= fla.flarec= 0;
rad= har->rad;
alfa= har->alfa;
visifac= R.ycor*(har->pixels);
/* all radials added / r^3 == 1.0! */
visifac /= (har->rad*har->rad*har->rad);
visifac*= visifac;
ma= har->mat;
/* first halo: just do */
har->rad= rad*ma->flaresize*visifac;
har->radsq= har->rad*har->rad;
har->zs= fla.zs= 0;
har->alfa= alfa*visifac;
renderhalo(har);
/* next halo's: the flares */
rc= hashvectf + ma->seed2;
for(b=1; b<har->flarec; b++) {
fla.r= fabs(rc[0]);
fla.g= fabs(rc[1]);
fla.b= fabs(rc[2]);
fla.alfa= ma->flareboost*fabs(alfa*visifac*rc[3]);
fla.hard= 20.0 + fabs(70*rc[7]);
fla.tex= 0;
type= (int)(fabs(3.9*rc[6]));
fla.rad= ma->subsize*sqrt(fabs(2.0*har->rad*rc[4]));
if(type==3) {
fla.rad*= 3.0;
fla.rad+= R.rectx/10;
}
fla.radsq= fla.rad*fla.rad;
vec[0]= 1.4*rc[5]*(har->xs-R.afmx);
vec[1]= 1.4*rc[5]*(har->ys-R.afmy);
vec[2]= 32.0*sqrt(vec[0]*vec[0] + vec[1]*vec[1] + 1.0);
fla.xs= R.afmx + vec[0] + (1.2+rc[8])*R.rectx*vec[0]/vec[2];
fla.ys= R.afmy + vec[1] + (1.2+rc[8])*R.rectx*vec[1]/vec[2];
if(R.flag & R_SEC_FIELD) {
if(R.r.mode & R_ODDFIELD) fla.ys += 0.5;
else fla.ys -= 0.5;
}
if(type & 1) fla.type= HA_FLARECIRC;
else fla.type= 0;
renderhalo(&fla);
fla.alfa*= 0.5;
if(type & 2) fla.type= HA_FLARECIRC;
else fla.type= 0;
renderhalo(&fla);
rc+= 7;
}
} /* end of void renderflare(HaloRen *har) */
void add_halo_flare(void)
{
/* extern void RE_projectverto(); */ /* zbuf.c */
HaloRen *har = NULL;
int a, mode;
mode= R.r.mode;
R.r.mode &= ~R_PANORAMA;
R.xstart= -R.afmx;
R.ystart= -R.afmy;
R.xend= R.xstart+R.rectx-1;
R.yend= R.ystart+R.recty-1;
RE_setwindowclip(1,-1); /* no jit:(-1) */
setzbufvlaggen(RE_projectverto);
for(a=0; a<R.tothalo; a++) {
if((a & 255)==0) har= R.bloha[a>>8];
else har++;
if(har->flarec) {
RE_renderflare(har);
}
}
R.r.mode= mode;
if(R.rectftot) RE_floatbuffer_to_output();
}
/* end of render.c */