when there was a tiny mismatch between low and high poly models, maybe because of float precision when editing the mesh. Added a small epsilon now to avoid this.
2785 lines
67 KiB
C
2785 lines
67 KiB
C
/*
|
|
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* Contributors: Hos, Robert Wenzlaff.
|
|
* Contributors: 2004/2005/2006 Blender Foundation, full recode
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/render/intern/source/rendercore.c
|
|
* \ingroup render
|
|
*/
|
|
|
|
|
|
/* system includes */
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
/* External modules: */
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_jitter.h"
|
|
#include "BLI_rand.h"
|
|
#include "BLI_threads.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_group_types.h"
|
|
|
|
#include "BKE_global.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_node.h"
|
|
#include "BKE_texture.h"
|
|
#include "BKE_scene.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_colormanagement.h"
|
|
|
|
/* local include */
|
|
#include "rayintersection.h"
|
|
#include "rayobject.h"
|
|
#include "renderpipeline.h"
|
|
#include "render_result.h"
|
|
#include "render_types.h"
|
|
#include "renderdatabase.h"
|
|
#include "occlusion.h"
|
|
#include "pixelblending.h"
|
|
#include "pixelshading.h"
|
|
#include "shadbuf.h"
|
|
#include "shading.h"
|
|
#include "sss.h"
|
|
#include "zbuf.h"
|
|
|
|
#include "PIL_time.h"
|
|
|
|
/* own include */
|
|
#include "rendercore.h"
|
|
|
|
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
/* 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;
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
/* x and y are current pixels in rect to be rendered */
|
|
/* do not normalize! */
|
|
void calc_view_vector(float view[3], float x, float y)
|
|
{
|
|
|
|
view[2]= -ABS(R.clipsta);
|
|
|
|
if (R.r.mode & R_ORTHO) {
|
|
view[0]= view[1]= 0.0f;
|
|
}
|
|
else {
|
|
|
|
if (R.r.mode & R_PANORAMA) {
|
|
x-= R.panodxp;
|
|
}
|
|
|
|
/* move x and y to real viewplane coords */
|
|
x = (x / (float)R.winx);
|
|
view[0] = R.viewplane.xmin + x * BLI_rctf_size_x(&R.viewplane);
|
|
|
|
y = (y / (float)R.winy);
|
|
view[1] = R.viewplane.ymin + y * BLI_rctf_size_y(&R.viewplane);
|
|
|
|
// 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+R.bluroffsy+0.5)*R.ycor;
|
|
|
|
if (R.r.mode & R_PANORAMA) {
|
|
float u= view[0] + R.panodxv; float v= view[2];
|
|
view[0]= R.panoco*u + R.panosi*v;
|
|
view[2]= -R.panosi*u + R.panoco*v;
|
|
}
|
|
}
|
|
}
|
|
|
|
void calc_renderco_ortho(float co[3], float x, float y, int z)
|
|
{
|
|
/* x and y 3d coordinate can be derived from pixel coord and winmat */
|
|
float fx= 2.0f/(R.winx*R.winmat[0][0]);
|
|
float fy= 2.0f/(R.winy*R.winmat[1][1]);
|
|
float zco;
|
|
|
|
co[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0];
|
|
co[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1];
|
|
|
|
zco= ((float)z)/2147483647.0f;
|
|
co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
|
|
}
|
|
|
|
void calc_renderco_zbuf(float co[3], const float view[3], int z)
|
|
{
|
|
float fac, zco;
|
|
|
|
/* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */
|
|
zco= ((float)z)/2147483647.0f;
|
|
co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
|
|
|
|
fac= co[2]/view[2];
|
|
co[0]= fac*view[0];
|
|
co[1]= fac*view[1];
|
|
}
|
|
|
|
/* also used in zbuf.c and shadbuf.c */
|
|
int count_mask(unsigned short mask)
|
|
{
|
|
if (R.samples)
|
|
return (R.samples->cmask[mask & 255]+R.samples->cmask[mask>>8]);
|
|
return 0;
|
|
}
|
|
|
|
static int calchalo_z(HaloRen *har, int zz)
|
|
{
|
|
|
|
if (har->type & HA_ONLYSKY) {
|
|
if (zz < 0x7FFFFFF0) zz= - 0x7FFFFF; /* edge render messes zvalues */
|
|
}
|
|
else {
|
|
zz= (zz>>8);
|
|
}
|
|
return zz;
|
|
}
|
|
|
|
|
|
|
|
static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, int od, float dist, float xn, float yn, PixStr *ps)
|
|
{
|
|
float col[4], accol[4], fac;
|
|
int amount, amountm, zz, flarec, sample, fullsample, mask=0;
|
|
|
|
fullsample= (totsample > 1);
|
|
amount= 0;
|
|
accol[0] = accol[1] = accol[2] = accol[3]= 0.0f;
|
|
col[0] = col[1] = col[2] = col[3]= 0.0f;
|
|
flarec= har->flarec;
|
|
|
|
while (ps) {
|
|
amountm= count_mask(ps->mask);
|
|
amount+= amountm;
|
|
|
|
zz= calchalo_z(har, ps->z);
|
|
if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
|
|
if (shadeHaloFloat(har, col, zz, dist, xn, yn, flarec)) {
|
|
flarec= 0;
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++)
|
|
if (ps->mask & (1 << sample))
|
|
addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
|
|
}
|
|
else {
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
|
|
mask |= ps->mask;
|
|
ps= ps->next;
|
|
}
|
|
|
|
/* now do the sky sub-pixels */
|
|
amount= R.osa-amount;
|
|
if (amount) {
|
|
if (shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec)) {
|
|
if (!fullsample) {
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++)
|
|
if (!(mask & (1 << sample)))
|
|
addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
|
|
}
|
|
else {
|
|
col[0]= accol[0];
|
|
col[1]= accol[1];
|
|
col[2]= accol[2];
|
|
col[3]= accol[3];
|
|
|
|
for (sample=0; sample<totsample; sample++)
|
|
addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
|
|
}
|
|
}
|
|
|
|
static void halo_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
HaloRen *har;
|
|
rcti disprect= pa->disprect, testrect= pa->disprect;
|
|
float dist, xsq, ysq, xn, yn;
|
|
float col[4];
|
|
intptr_t *rd= NULL;
|
|
int a, *rz, zz, y, sample, totsample, od;
|
|
short minx, maxx, miny, maxy, x;
|
|
unsigned int lay= rl->lay;
|
|
|
|
/* we don't render halos in the cropped area, gives errors in flare counter */
|
|
if (pa->crop) {
|
|
testrect.xmin+= pa->crop;
|
|
testrect.xmax-= pa->crop;
|
|
testrect.ymin+= pa->crop;
|
|
testrect.ymax-= pa->crop;
|
|
}
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (a=0; a<R.tothalo; a++) {
|
|
har= R.sortedhalos[a];
|
|
|
|
/* layer test, clip halo with y */
|
|
if ((har->lay & lay) == 0) {
|
|
/* pass */
|
|
}
|
|
else if (testrect.ymin > har->maxy) {
|
|
/* pass */
|
|
}
|
|
else if (testrect.ymax < har->miny) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
|
|
minx= floor(har->xs-har->rad);
|
|
maxx= ceil(har->xs+har->rad);
|
|
|
|
if (testrect.xmin > maxx) {
|
|
/* pass */
|
|
}
|
|
else if (testrect.xmax < minx) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
|
|
minx = max_ii(minx, testrect.xmin);
|
|
maxx = min_ii(maxx, testrect.xmax);
|
|
|
|
miny = max_ii(har->miny, testrect.ymin);
|
|
maxy = min_ii(har->maxy, testrect.ymax);
|
|
|
|
for (y=miny; y<maxy; y++) {
|
|
int rectofs= (y-disprect.ymin)*pa->rectx + (minx - disprect.xmin);
|
|
rz= pa->rectz + rectofs;
|
|
od= rectofs;
|
|
|
|
if (pa->rectdaps)
|
|
rd= pa->rectdaps + rectofs;
|
|
|
|
yn= (y-har->ys)*R.ycor;
|
|
ysq= yn*yn;
|
|
|
|
for (x=minx; x<maxx; x++, rz++, od++) {
|
|
xn= x- har->xs;
|
|
xsq= xn*xn;
|
|
dist= xsq+ysq;
|
|
if (dist<har->radsq) {
|
|
if (rd && *rd) {
|
|
halo_pixelstruct(har, rlpp, totsample, od, dist, xn, yn, (PixStr *)*rd);
|
|
}
|
|
else {
|
|
zz= calchalo_z(har, *rz);
|
|
if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
|
|
if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) {
|
|
for (sample=0; sample<totsample; sample++)
|
|
addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (rd) rd++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (R.test_break(R.tbh) ) break;
|
|
}
|
|
}
|
|
|
|
static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
ShadeInput shi;
|
|
float *pass;
|
|
float fac, col[4];
|
|
intptr_t *rd= pa->rectdaps;
|
|
int *rz= pa->rectz;
|
|
int x, y, sample, totsample, fullsample, od;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
fullsample= (totsample > 1);
|
|
|
|
shade_input_initialize(&shi, pa, rl, 0); /* this zero's ShadeInput for us */
|
|
|
|
for (od=0, y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, rz++, od++) {
|
|
|
|
calc_view_vector(shi.view, x, y);
|
|
|
|
if (rd && *rd) {
|
|
PixStr *ps= (PixStr *)*rd;
|
|
int count, totsamp= 0, mask= 0;
|
|
|
|
while (ps) {
|
|
if (R.r.mode & R_ORTHO)
|
|
calc_renderco_ortho(shi.co, (float)x, (float)y, ps->z);
|
|
else
|
|
calc_renderco_zbuf(shi.co, shi.view, ps->z);
|
|
|
|
totsamp+= count= count_mask(ps->mask);
|
|
mask |= ps->mask;
|
|
|
|
col[0]= col[1]= col[2]= col[3]= 0.0f;
|
|
renderspothalo(&shi, col, 1.0f);
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++) {
|
|
if (ps->mask & (1 << sample)) {
|
|
pass= rlpp[sample]->rectf + od*4;
|
|
pass[0]+= col[0];
|
|
pass[1]+= col[1];
|
|
pass[2]+= col[2];
|
|
pass[3]+= col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fac= ((float)count)/(float)R.osa;
|
|
pass= rl->rectf + od*4;
|
|
pass[0]+= fac*col[0];
|
|
pass[1]+= fac*col[1];
|
|
pass[2]+= fac*col[2];
|
|
pass[3]+= fac*col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
|
|
ps= ps->next;
|
|
}
|
|
|
|
if (totsamp<R.osa) {
|
|
shi.co[2]= 0.0f;
|
|
|
|
col[0]= col[1]= col[2]= col[3]= 0.0f;
|
|
renderspothalo(&shi, col, 1.0f);
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++) {
|
|
if (!(mask & (1 << sample))) {
|
|
pass= rlpp[sample]->rectf + od*4;
|
|
pass[0]+= col[0];
|
|
pass[1]+= col[1];
|
|
pass[2]+= col[2];
|
|
pass[3]+= col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fac= ((float)R.osa-totsamp)/(float)R.osa;
|
|
pass= rl->rectf + od*4;
|
|
pass[0]+= fac*col[0];
|
|
pass[1]+= fac*col[1];
|
|
pass[2]+= fac*col[2];
|
|
pass[3]+= fac*col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (R.r.mode & R_ORTHO)
|
|
calc_renderco_ortho(shi.co, (float)x, (float)y, *rz);
|
|
else
|
|
calc_renderco_zbuf(shi.co, shi.view, *rz);
|
|
|
|
col[0]= col[1]= col[2]= col[3]= 0.0f;
|
|
renderspothalo(&shi, col, 1.0f);
|
|
|
|
for (sample=0; sample<totsample; sample++) {
|
|
pass= rlpp[sample]->rectf + od*4;
|
|
pass[0]+= col[0];
|
|
pass[1]+= col[1];
|
|
pass[2]+= col[2];
|
|
pass[3]+= col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
|
|
if (rd) rd++;
|
|
}
|
|
if (y&1)
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
}
|
|
|
|
|
|
/* ********************* MAINLOOPS ******************** */
|
|
|
|
/* osa version */
|
|
static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, ShadeInput *shi, ShadeResult *shr)
|
|
{
|
|
RenderPass *rpass;
|
|
|
|
/* combined rgb */
|
|
add_filt_fmask(curmask, shr->combined, rl->rectf + 4*offset, rectx);
|
|
|
|
for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
|
|
float *fp, *col= NULL;
|
|
int pixsize= 3;
|
|
|
|
switch (rpass->passtype) {
|
|
case SCE_PASS_Z:
|
|
fp= rpass->rect + offset;
|
|
*fp= shr->z;
|
|
break;
|
|
case SCE_PASS_RGBA:
|
|
col= shr->col;
|
|
pixsize= 4;
|
|
break;
|
|
case SCE_PASS_EMIT:
|
|
col= shr->emit;
|
|
break;
|
|
case SCE_PASS_DIFFUSE:
|
|
col= shr->diff;
|
|
break;
|
|
case SCE_PASS_SPEC:
|
|
col= shr->spec;
|
|
break;
|
|
case SCE_PASS_SHADOW:
|
|
col= shr->shad;
|
|
break;
|
|
case SCE_PASS_AO:
|
|
col= shr->ao;
|
|
break;
|
|
case SCE_PASS_ENVIRONMENT:
|
|
col= shr->env;
|
|
break;
|
|
case SCE_PASS_INDIRECT:
|
|
col= shr->indirect;
|
|
break;
|
|
case SCE_PASS_REFLECT:
|
|
col= shr->refl;
|
|
break;
|
|
case SCE_PASS_REFRACT:
|
|
col= shr->refr;
|
|
break;
|
|
case SCE_PASS_NORMAL:
|
|
col= shr->nor;
|
|
break;
|
|
case SCE_PASS_UV:
|
|
/* box filter only, gauss will screwup UV too much */
|
|
if (shi->totuv) {
|
|
float mult= (float)count_mask(curmask)/(float)R.osa;
|
|
fp= rpass->rect + 3*offset;
|
|
fp[0]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[0]);
|
|
fp[1]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[1]);
|
|
fp[2]+= mult;
|
|
}
|
|
break;
|
|
case SCE_PASS_INDEXOB:
|
|
/* no filter */
|
|
if (shi->vlr) {
|
|
fp= rpass->rect + offset;
|
|
if (*fp==0.0f)
|
|
*fp= (float)shi->obr->ob->index;
|
|
}
|
|
break;
|
|
case SCE_PASS_INDEXMA:
|
|
/* no filter */
|
|
if (shi->vlr) {
|
|
fp= rpass->rect + offset;
|
|
if (*fp==0.0f)
|
|
*fp= (float)shi->mat->index;
|
|
}
|
|
break;
|
|
case SCE_PASS_MIST:
|
|
/* */
|
|
col= &shr->mist;
|
|
pixsize= 1;
|
|
break;
|
|
|
|
case SCE_PASS_VECTOR:
|
|
{
|
|
/* add minimum speed in pixel, no filter */
|
|
fp= rpass->rect + 4*offset;
|
|
if ( (ABS(shr->winspeed[0]) + ABS(shr->winspeed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) {
|
|
fp[0]= shr->winspeed[0];
|
|
fp[1]= shr->winspeed[1];
|
|
}
|
|
if ( (ABS(shr->winspeed[2]) + ABS(shr->winspeed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) {
|
|
fp[2]= shr->winspeed[2];
|
|
fp[3]= shr->winspeed[3];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCE_PASS_RAYHITS:
|
|
/* */
|
|
col= shr->rayhits;
|
|
pixsize= 4;
|
|
break;
|
|
}
|
|
if (col) {
|
|
fp= rpass->rect + pixsize*offset;
|
|
add_filt_fmask_pixsize(curmask, col, fp, rectx, pixsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* non-osa version */
|
|
static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *shr)
|
|
{
|
|
RenderPass *rpass;
|
|
float *fp;
|
|
|
|
fp= rl->rectf + 4*offset;
|
|
copy_v4_v4(fp, shr->combined);
|
|
|
|
for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
|
|
float *col= NULL, uvcol[3];
|
|
int a, pixsize= 3;
|
|
|
|
switch (rpass->passtype) {
|
|
case SCE_PASS_Z:
|
|
fp= rpass->rect + offset;
|
|
*fp= shr->z;
|
|
break;
|
|
case SCE_PASS_RGBA:
|
|
col= shr->col;
|
|
pixsize= 4;
|
|
break;
|
|
case SCE_PASS_EMIT:
|
|
col= shr->emit;
|
|
break;
|
|
case SCE_PASS_DIFFUSE:
|
|
col= shr->diff;
|
|
break;
|
|
case SCE_PASS_SPEC:
|
|
col= shr->spec;
|
|
break;
|
|
case SCE_PASS_SHADOW:
|
|
col= shr->shad;
|
|
break;
|
|
case SCE_PASS_AO:
|
|
col= shr->ao;
|
|
break;
|
|
case SCE_PASS_ENVIRONMENT:
|
|
col= shr->env;
|
|
break;
|
|
case SCE_PASS_INDIRECT:
|
|
col= shr->indirect;
|
|
break;
|
|
case SCE_PASS_REFLECT:
|
|
col= shr->refl;
|
|
break;
|
|
case SCE_PASS_REFRACT:
|
|
col= shr->refr;
|
|
break;
|
|
case SCE_PASS_NORMAL:
|
|
col= shr->nor;
|
|
break;
|
|
case SCE_PASS_UV:
|
|
if (shi->totuv) {
|
|
uvcol[0]= 0.5f + 0.5f*shi->uv[shi->actuv].uv[0];
|
|
uvcol[1]= 0.5f + 0.5f*shi->uv[shi->actuv].uv[1];
|
|
uvcol[2]= 1.0f;
|
|
col= uvcol;
|
|
}
|
|
break;
|
|
case SCE_PASS_VECTOR:
|
|
col= shr->winspeed;
|
|
pixsize= 4;
|
|
break;
|
|
case SCE_PASS_INDEXOB:
|
|
if (shi->vlr) {
|
|
fp= rpass->rect + offset;
|
|
*fp= (float)shi->obr->ob->index;
|
|
}
|
|
break;
|
|
case SCE_PASS_INDEXMA:
|
|
if (shi->vlr) {
|
|
fp= rpass->rect + offset;
|
|
*fp= (float)shi->mat->index;
|
|
}
|
|
break;
|
|
case SCE_PASS_MIST:
|
|
fp= rpass->rect + offset;
|
|
*fp= shr->mist;
|
|
break;
|
|
case SCE_PASS_RAYHITS:
|
|
col= shr->rayhits;
|
|
pixsize= 4;
|
|
break;
|
|
}
|
|
if (col) {
|
|
fp= rpass->rect + pixsize*offset;
|
|
for (a=0; a<pixsize; a++)
|
|
fp[a]= col[a];
|
|
}
|
|
}
|
|
}
|
|
|
|
int get_sample_layers(RenderPart *pa, RenderLayer *rl, RenderLayer **rlpp)
|
|
{
|
|
|
|
if (pa->fullresult.first) {
|
|
int sample, nr= BLI_findindex(&pa->result->layers, rl);
|
|
|
|
for (sample=0; sample<R.osa; sample++) {
|
|
RenderResult *rr= BLI_findlink(&pa->fullresult, sample);
|
|
|
|
rlpp[sample]= BLI_findlink(&rr->layers, nr);
|
|
}
|
|
return R.osa;
|
|
}
|
|
else {
|
|
rlpp[0]= rl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* only do sky, is default in the solid layer (shade_tile) btw */
|
|
static void sky_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int x, y, od=0, totsample;
|
|
|
|
if (R.r.alphamode!=R_ADDSKY)
|
|
return;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od+=4) {
|
|
float col[4];
|
|
int sample, done = FALSE;
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
float *pass= rlpp[sample]->rectf + od;
|
|
|
|
if (pass[3]<1.0f) {
|
|
|
|
if (done==0) {
|
|
shadeSkyPixel(col, x, y, pa->thread);
|
|
done = TRUE;
|
|
}
|
|
|
|
if (pass[3]==0.0f) {
|
|
copy_v4_v4(pass, col);
|
|
}
|
|
else {
|
|
addAlphaUnderFloat(pass, col);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (y&1)
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
}
|
|
|
|
static void atm_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderPass *zpass;
|
|
GroupObject *go;
|
|
LampRen *lar;
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int totsample;
|
|
int x, y, od= 0;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
/* check that z pass is enabled */
|
|
if (pa->rectz==NULL) return;
|
|
for (zpass= rl->passes.first; zpass; zpass= zpass->next)
|
|
if (zpass->passtype==SCE_PASS_Z)
|
|
break;
|
|
|
|
if (zpass==NULL) return;
|
|
|
|
/* check for at least one sun lamp that its atmosphere flag is enabled */
|
|
for (go=R.lights.first; go; go= go->next) {
|
|
lar= go->lampren;
|
|
if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_AP))
|
|
break;
|
|
}
|
|
/* do nothign and return if there is no sun lamp */
|
|
if (go==NULL)
|
|
return;
|
|
|
|
/* for each x,y and each sample, and each sun lamp*/
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od++) {
|
|
int sample;
|
|
|
|
for (sample=0; sample<totsample; sample++) {
|
|
float *zrect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z) + od;
|
|
float *rgbrect = rlpp[sample]->rectf + 4*od;
|
|
float rgb[3] = {0};
|
|
int done = FALSE;
|
|
|
|
for (go=R.lights.first; go; go= go->next) {
|
|
|
|
|
|
lar= go->lampren;
|
|
if (lar->type==LA_SUN && lar->sunsky) {
|
|
|
|
/* if it's sky continue and don't apply atmosphere effect on it */
|
|
if (*zrect >= 9.9e10f || rgbrect[3]==0.0f) {
|
|
continue;
|
|
}
|
|
|
|
if ((lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) {
|
|
float tmp_rgb[3];
|
|
|
|
/* skip if worldspace lamp vector is below horizon */
|
|
if (go->ob->obmat[2][2] < 0.f) {
|
|
continue;
|
|
}
|
|
|
|
copy_v3_v3(tmp_rgb, rgbrect);
|
|
if (rgbrect[3]!=1.0f) { /* de-premul */
|
|
mul_v3_fl(tmp_rgb, 1.0f/rgbrect[3]);
|
|
}
|
|
shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect);
|
|
if (rgbrect[3]!=1.0f) { /* premul */
|
|
mul_v3_fl(tmp_rgb, rgbrect[3]);
|
|
}
|
|
|
|
if (done==0) {
|
|
copy_v3_v3(rgb, tmp_rgb);
|
|
done = TRUE;
|
|
}
|
|
else {
|
|
rgb[0] = 0.5f*rgb[0] + 0.5f*tmp_rgb[0];
|
|
rgb[1] = 0.5f*rgb[1] + 0.5f*tmp_rgb[1];
|
|
rgb[2] = 0.5f*rgb[2] + 0.5f*tmp_rgb[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if at least for one sun lamp aerial perspective was applied*/
|
|
if (done) {
|
|
copy_v3_v3(rgbrect, rgb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void shadeDA_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderResult *rr= pa->result;
|
|
ShadeSample ssamp;
|
|
intptr_t *rd, *rectdaps= pa->rectdaps;
|
|
int samp;
|
|
int x, y, seed, crop=0, offs=0, od;
|
|
|
|
if (R.test_break(R.tbh)) return;
|
|
|
|
/* irregular shadowb buffer creation */
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_create(pa, NULL);
|
|
|
|
/* we set per pixel a fixed seed, for random AO and shadow samples */
|
|
seed= pa->rectx*pa->disprect.ymin;
|
|
|
|
/* general shader info, passes */
|
|
shade_sample_initialize(&ssamp, pa, rl);
|
|
|
|
/* occlusion caching */
|
|
if (R.occlusiontree)
|
|
cache_occ_samples(&R, pa, &ssamp);
|
|
|
|
/* filtered render, for now we assume only 1 filter size */
|
|
if (pa->crop) {
|
|
crop= 1;
|
|
rectdaps+= pa->rectx + 1;
|
|
offs= pa->rectx + 1;
|
|
}
|
|
|
|
/* scanline updates have to be 2 lines behind */
|
|
rr->renrect.ymin = 0;
|
|
rr->renrect.ymax = -2*crop;
|
|
rr->renlay= rl;
|
|
|
|
for (y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) {
|
|
rd= rectdaps;
|
|
od= offs;
|
|
|
|
for (x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, rd++, od++) {
|
|
BLI_thread_srandom(pa->thread, seed++);
|
|
|
|
if (*rd) {
|
|
if (shade_samples(&ssamp, (PixStr *)(*rd), x, y)) {
|
|
|
|
/* multisample buffers or filtered mask filling? */
|
|
if (pa->fullresult.first) {
|
|
int a;
|
|
for (samp=0; samp<ssamp.tot; samp++) {
|
|
int smask= ssamp.shi[samp].mask;
|
|
for (a=0; a<R.osa; a++) {
|
|
int mask= 1<<a;
|
|
if (smask & mask)
|
|
add_passes(ssamp.rlpp[a], od, &ssamp.shi[samp], &ssamp.shr[samp]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (samp=0; samp<ssamp.tot; samp++)
|
|
add_filt_passes(rl, ssamp.shi[samp].mask, pa->rectx, od, &ssamp.shi[samp], &ssamp.shr[samp]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rectdaps+= pa->rectx;
|
|
offs+= pa->rectx;
|
|
|
|
if (y&1) if (R.test_break(R.tbh)) break;
|
|
}
|
|
|
|
/* disable scanline updating */
|
|
rr->renlay= NULL;
|
|
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_free(pa);
|
|
|
|
if (R.occlusiontree)
|
|
free_occ_samples(&R, pa);
|
|
}
|
|
|
|
/* ************* pixel struct ******** */
|
|
|
|
|
|
static PixStrMain *addpsmain(ListBase *lb)
|
|
{
|
|
PixStrMain *psm;
|
|
|
|
psm= (PixStrMain *)MEM_mallocN(sizeof(PixStrMain), "pixstrMain");
|
|
BLI_addtail(lb, psm);
|
|
|
|
psm->ps= (PixStr *)MEM_mallocN(4096*sizeof(PixStr), "pixstr");
|
|
psm->counter= 0;
|
|
|
|
return psm;
|
|
}
|
|
|
|
static void freeps(ListBase *lb)
|
|
{
|
|
PixStrMain *psm, *psmnext;
|
|
|
|
for (psm= lb->first; psm; psm= psmnext) {
|
|
psmnext= psm->next;
|
|
if (psm->ps)
|
|
MEM_freeN(psm->ps);
|
|
MEM_freeN(psm);
|
|
}
|
|
lb->first= lb->last= NULL;
|
|
}
|
|
|
|
static void addps(ListBase *lb, intptr_t *rd, int obi, int facenr, int z, int maskz, unsigned short mask)
|
|
{
|
|
PixStrMain *psm;
|
|
PixStr *ps, *last= NULL;
|
|
|
|
if (*rd) {
|
|
ps= (PixStr *)(*rd);
|
|
|
|
while (ps) {
|
|
if ( ps->obi == obi && ps->facenr == facenr ) {
|
|
ps->mask |= mask;
|
|
return;
|
|
}
|
|
last= ps;
|
|
ps= ps->next;
|
|
}
|
|
}
|
|
|
|
/* make new PS (pixel struct) */
|
|
psm= lb->last;
|
|
|
|
if (psm->counter==4095)
|
|
psm= addpsmain(lb);
|
|
|
|
ps= psm->ps + psm->counter++;
|
|
|
|
if (last) last->next= ps;
|
|
else *rd= (intptr_t)ps;
|
|
|
|
ps->next= NULL;
|
|
ps->obi= obi;
|
|
ps->facenr= facenr;
|
|
ps->z= z;
|
|
ps->maskz= maskz;
|
|
ps->mask = mask;
|
|
ps->shadfac= 0;
|
|
}
|
|
|
|
static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect)
|
|
{
|
|
float addcol[4];
|
|
int pix;
|
|
|
|
if (arect==NULL)
|
|
return;
|
|
|
|
for (pix= pa->rectx*pa->recty; pix>0; pix--, arect++, rectf+=4) {
|
|
if (*arect != 0.0f) {
|
|
addcol[0]= *arect * R.r.edgeR;
|
|
addcol[1]= *arect * R.r.edgeG;
|
|
addcol[2]= *arect * R.r.edgeB;
|
|
addcol[3]= *arect;
|
|
addAlphaOverFloat(rectf, addcol);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void convert_to_key_alpha(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int y, sample, totsample;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
float *rectf= rlpp[sample]->rectf;
|
|
|
|
for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) {
|
|
if (rectf[3] >= 1.0f) {
|
|
/* pass */
|
|
}
|
|
else if (rectf[3] > 0.0f) {
|
|
rectf[0] /= rectf[3];
|
|
rectf[1] /= rectf[3];
|
|
rectf[2] /= rectf[3];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */
|
|
static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int y, sample, totsample;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
/* not for full sample, there we clamp after compositing */
|
|
if (totsample > 1)
|
|
return;
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
float *rectf= rlpp[sample]->rectf;
|
|
|
|
for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) {
|
|
rectf[0] = MAX2(rectf[0], 0.0f);
|
|
rectf[1] = MAX2(rectf[1], 0.0f);
|
|
rectf[2] = MAX2(rectf[2], 0.0f);
|
|
CLAMP(rectf[3], 0.0f, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* adds only alpha values */
|
|
static void edge_enhance_tile(RenderPart *pa, float *rectf, int *rectz)
|
|
{
|
|
/* use zbuffer to define edges, add it to the image */
|
|
int y, x, col, *rz, *rz1, *rz2, *rz3;
|
|
int zval1, zval2, zval3;
|
|
float *rf;
|
|
|
|
/* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */
|
|
rz= rectz;
|
|
if (rz==NULL) return;
|
|
|
|
for (y=0; y<pa->recty; y++)
|
|
for (x=0; x<pa->rectx; x++, rz++) (*rz)>>= 4;
|
|
|
|
rz1= rectz;
|
|
rz2= rz1+pa->rectx;
|
|
rz3= rz2+pa->rectx;
|
|
|
|
rf= rectf+pa->rectx+1;
|
|
|
|
for (y=0; y<pa->recty-2; y++) {
|
|
for (x=0; x<pa->rectx-2; x++, rz1++, rz2++, rz3++, rf++) {
|
|
|
|
/* 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= ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 );
|
|
if (col<0) col= -col;
|
|
|
|
col >>= 5;
|
|
if (col > (1<<16)) col= (1<<16);
|
|
else col= (R.r.edgeint*col)>>8;
|
|
|
|
if (col>0) {
|
|
float fcol;
|
|
|
|
if (col>255) fcol= 1.0f;
|
|
else fcol= (float)col/255.0f;
|
|
|
|
if (R.osa)
|
|
*rf+= fcol/(float)R.osa;
|
|
else
|
|
*rf= fcol;
|
|
}
|
|
}
|
|
rz1+= 2;
|
|
rz2+= 2;
|
|
rz3+= 2;
|
|
rf+= 2;
|
|
}
|
|
|
|
/* shift back zbuf values, we might need it still */
|
|
rz= rectz;
|
|
for (y=0; y<pa->recty; y++)
|
|
for (x=0; x<pa->rectx; x++, rz++) (*rz)<<= 4;
|
|
|
|
}
|
|
|
|
static void reset_sky_speed(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
/* for all pixels with max speed, set to zero */
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
float *fp;
|
|
int a, sample, totsample;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR);
|
|
if (fp==NULL) break;
|
|
|
|
for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--)
|
|
if (fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f;
|
|
}
|
|
}
|
|
|
|
static unsigned short *make_solid_mask(RenderPart *pa)
|
|
{
|
|
intptr_t *rd= pa->rectdaps;
|
|
unsigned short *solidmask, *sp;
|
|
int x;
|
|
|
|
if (rd==NULL) return NULL;
|
|
|
|
sp=solidmask= MEM_mallocN(sizeof(short)*pa->rectx*pa->recty, "solidmask");
|
|
|
|
for (x=pa->rectx*pa->recty; x>0; x--, rd++, sp++) {
|
|
if (*rd) {
|
|
PixStr *ps= (PixStr *)*rd;
|
|
|
|
*sp= ps->mask;
|
|
for (ps= ps->next; ps; ps= ps->next)
|
|
*sp |= ps->mask;
|
|
}
|
|
else
|
|
*sp= 0;
|
|
}
|
|
|
|
return solidmask;
|
|
}
|
|
|
|
static void addAlphaOverFloatMask(float *dest, float *source, unsigned short dmask, unsigned short smask)
|
|
{
|
|
unsigned short shared= dmask & smask;
|
|
float mul= 1.0f - source[3];
|
|
|
|
if (shared) { /* overlapping masks */
|
|
|
|
/* masks differ, we make a mixture of 'add' and 'over' */
|
|
if (shared!=dmask) {
|
|
float shared_bits= (float)count_mask(shared); /* alpha over */
|
|
float tot_bits= (float)count_mask(smask|dmask); /* alpha add */
|
|
|
|
float add= (tot_bits - shared_bits)/tot_bits; /* add level */
|
|
mul= add + (1.0f-add)*mul;
|
|
}
|
|
}
|
|
else if (dmask && smask) {
|
|
/* works for premul only, of course */
|
|
dest[0]+= source[0];
|
|
dest[1]+= source[1];
|
|
dest[2]+= source[2];
|
|
dest[3]+= source[3];
|
|
|
|
return;
|
|
}
|
|
|
|
dest[0]= (mul*dest[0]) + source[0];
|
|
dest[1]= (mul*dest[1]) + source[1];
|
|
dest[2]= (mul*dest[2]) + source[2];
|
|
dest[3]= (mul*dest[3]) + source[3];
|
|
}
|
|
|
|
typedef struct ZbufSolidData {
|
|
RenderLayer *rl;
|
|
ListBase *psmlist;
|
|
float *edgerect;
|
|
} ZbufSolidData;
|
|
|
|
static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data)
|
|
{
|
|
ZbufSolidData *sdata= (ZbufSolidData*)data;
|
|
ListBase *lb= sdata->psmlist;
|
|
intptr_t *rd= pa->rectdaps;
|
|
int *ro= zspan->recto;
|
|
int *rp= zspan->rectp;
|
|
int *rz= zspan->rectz;
|
|
int *rm= zspan->rectmask;
|
|
int x, y;
|
|
int mask= 1<<sample;
|
|
|
|
for (y=0; y<pa->recty; y++) {
|
|
for (x=0; x<pa->rectx; x++, rd++, rp++, ro++, rz++, rm++) {
|
|
if (*rp) {
|
|
addps(lb, rd, *ro, *rp, *rz, (zspan->rectmask)? *rm: 0, mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sdata->rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edge_enhance_tile(pa, sdata->edgerect, zspan->rectz);
|
|
}
|
|
|
|
/* main call for shading Delta Accum, for OSA */
|
|
/* supposed to be fully threadable! */
|
|
void zbufshadeDA_tile(RenderPart *pa)
|
|
{
|
|
RenderResult *rr= pa->result;
|
|
RenderLayer *rl;
|
|
ListBase psmlist= {NULL, NULL};
|
|
float *edgerect= NULL;
|
|
|
|
/* allocate the necessary buffers */
|
|
/* zbuffer inits these rects */
|
|
pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
|
|
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
|
|
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
|
|
for (rl= rr->layers.first; rl; rl= rl->next) {
|
|
if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
|
|
pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
|
|
|
|
/* initialize pixelstructs and edge buffer */
|
|
addpsmain(&psmlist);
|
|
pa->rectdaps= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "zbufDArectd");
|
|
|
|
if (rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge");
|
|
|
|
/* always fill visibility */
|
|
for (pa->sample=0; pa->sample<R.osa; pa->sample+=4) {
|
|
ZbufSolidData sdata;
|
|
|
|
sdata.rl= rl;
|
|
sdata.psmlist= &psmlist;
|
|
sdata.edgerect= edgerect;
|
|
zbuffer_solid(pa, rl, make_pixelstructs, &sdata);
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
|
|
/* shades solid */
|
|
if (rl->layflag & SCE_LAY_SOLID)
|
|
shadeDA_tile(pa, rl);
|
|
|
|
/* lamphalo after solid, before ztra, looks nicest because ztra does own halo */
|
|
if (R.flag & R_LAMPHALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
lamphalo_tile(pa, rl);
|
|
|
|
/* halo before ztra, because ztra fills in zbuffer now */
|
|
if (R.flag & R_HALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
halo_tile(pa, rl);
|
|
|
|
/* transp layer */
|
|
if (R.flag & R_ZTRA || R.totstrand) {
|
|
if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
|
|
if (pa->fullresult.first) {
|
|
zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist);
|
|
}
|
|
else {
|
|
unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */
|
|
|
|
/* allocate, but not free here, for asynchronous display of this rect in main thread */
|
|
rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
|
|
|
|
/* swap for live updates, and it is used in zbuf.c!!! */
|
|
SWAP(float *, rl->acolrect, rl->rectf);
|
|
ztramask= zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist);
|
|
SWAP(float *, rl->acolrect, rl->rectf);
|
|
|
|
/* zbuffer transp only returns ztramask if there's solid rendered */
|
|
if (ztramask)
|
|
solidmask= make_solid_mask(pa);
|
|
|
|
if (ztramask && solidmask) {
|
|
unsigned short *sps= solidmask, *spz= ztramask;
|
|
unsigned short fullmask= (1<<R.osa)-1;
|
|
float *fcol= rl->rectf; float *acol= rl->acolrect;
|
|
int x;
|
|
|
|
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) {
|
|
if (*sps == fullmask)
|
|
addAlphaOverFloat(fcol, acol);
|
|
else
|
|
addAlphaOverFloatMask(fcol, acol, *sps, *spz);
|
|
}
|
|
}
|
|
else {
|
|
float *fcol= rl->rectf; float *acol= rl->acolrect;
|
|
int x;
|
|
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
|
|
addAlphaOverFloat(fcol, acol);
|
|
}
|
|
}
|
|
if (solidmask) MEM_freeN(solidmask);
|
|
if (ztramask) MEM_freeN(ztramask);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* sun/sky */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
atm_tile(pa, rl);
|
|
|
|
/* sky before edge */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
sky_tile(pa, rl);
|
|
|
|
/* extra layers */
|
|
if (rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edge_enhance_add(pa, rl->rectf, edgerect);
|
|
|
|
if (rl->passflag & SCE_PASS_VECTOR)
|
|
reset_sky_speed(pa, rl);
|
|
|
|
/* clamp alpha to 0..1 range, can go outside due to filter */
|
|
clamp_alpha_rgb_range(pa, rl);
|
|
|
|
/* de-premul alpha */
|
|
if (R.r.alphamode & R_ALPHAKEY)
|
|
convert_to_key_alpha(pa, rl);
|
|
|
|
/* free stuff within loop! */
|
|
MEM_freeN(pa->rectdaps); pa->rectdaps= NULL;
|
|
freeps(&psmlist);
|
|
|
|
if (edgerect) MEM_freeN(edgerect);
|
|
edgerect= NULL;
|
|
|
|
if (pa->rectmask) {
|
|
MEM_freeN(pa->rectmask);
|
|
pa->rectmask= NULL;
|
|
}
|
|
}
|
|
|
|
/* free all */
|
|
MEM_freeN(pa->recto); pa->recto= NULL;
|
|
MEM_freeN(pa->rectp); pa->rectp= NULL;
|
|
MEM_freeN(pa->rectz); pa->rectz= NULL;
|
|
|
|
/* display active layer */
|
|
rr->renrect.ymin=rr->renrect.ymax = 0;
|
|
rr->renlay= render_get_active_layer(&R, rr);
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/* non OSA case, full tile render */
|
|
/* supposed to be fully threadable! */
|
|
void zbufshade_tile(RenderPart *pa)
|
|
{
|
|
ShadeSample ssamp;
|
|
RenderResult *rr= pa->result;
|
|
RenderLayer *rl;
|
|
PixStr ps;
|
|
float *edgerect= NULL;
|
|
|
|
/* fake pixel struct, to comply to osa render */
|
|
ps.next= NULL;
|
|
ps.mask= 0xFFFF;
|
|
|
|
/* zbuffer code clears/inits rects */
|
|
pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
|
|
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
|
|
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
|
|
|
|
for (rl= rr->layers.first; rl; rl= rl->next) {
|
|
if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
|
|
pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
|
|
|
|
/* general shader info, passes */
|
|
shade_sample_initialize(&ssamp, pa, rl);
|
|
|
|
zbuffer_solid(pa, rl, NULL, NULL);
|
|
|
|
if (!R.test_break(R.tbh)) { /* NOTE: this if () is not consistent */
|
|
|
|
/* edges only for solid part, ztransp doesn't support it yet anti-aliased */
|
|
if (rl->layflag & SCE_LAY_EDGE) {
|
|
if (R.r.mode & R_EDGE) {
|
|
edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge");
|
|
edge_enhance_tile(pa, edgerect, pa->rectz);
|
|
}
|
|
}
|
|
|
|
/* initialize scanline updates for main thread */
|
|
rr->renrect.ymin = 0;
|
|
rr->renlay= rl;
|
|
|
|
if (rl->layflag & SCE_LAY_SOLID) {
|
|
float *fcol= rl->rectf;
|
|
int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz;
|
|
int x, y, offs=0, seed;
|
|
|
|
/* we set per pixel a fixed seed, for random AO and shadow samples */
|
|
seed= pa->rectx*pa->disprect.ymin;
|
|
|
|
/* irregular shadowb buffer creation */
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_create(pa, NULL);
|
|
|
|
if (R.occlusiontree)
|
|
cache_occ_samples(&R, pa, &ssamp);
|
|
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, ro++, rz++, rp++, fcol+=4, offs++) {
|
|
/* per pixel fixed seed */
|
|
BLI_thread_srandom(pa->thread, seed++);
|
|
|
|
if (*rp) {
|
|
ps.obi= *ro;
|
|
ps.facenr= *rp;
|
|
ps.z= *rz;
|
|
if (shade_samples(&ssamp, &ps, x, y)) {
|
|
/* combined and passes */
|
|
add_passes(rl, offs, ssamp.shi, ssamp.shr);
|
|
}
|
|
}
|
|
}
|
|
if (y&1)
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
|
|
if (R.occlusiontree)
|
|
free_occ_samples(&R, pa);
|
|
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_free(pa);
|
|
}
|
|
|
|
/* disable scanline updating */
|
|
rr->renlay= NULL;
|
|
}
|
|
|
|
/* lamphalo after solid, before ztra, looks nicest because ztra does own halo */
|
|
if (R.flag & R_LAMPHALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
lamphalo_tile(pa, rl);
|
|
|
|
/* halo before ztra, because ztra fills in zbuffer now */
|
|
if (R.flag & R_HALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
halo_tile(pa, rl);
|
|
|
|
if (R.flag & R_ZTRA || R.totstrand) {
|
|
if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
|
|
float *fcol, *acol;
|
|
int x;
|
|
|
|
/* allocate, but not free here, for asynchronous display of this rect in main thread */
|
|
rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
|
|
|
|
/* swap for live updates */
|
|
SWAP(float *, rl->acolrect, rl->rectf);
|
|
zbuffer_transp_shade(pa, rl, rl->rectf, NULL);
|
|
SWAP(float *, rl->acolrect, rl->rectf);
|
|
|
|
fcol= rl->rectf; acol= rl->acolrect;
|
|
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
|
|
addAlphaOverFloat(fcol, acol);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* sun/sky */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
atm_tile(pa, rl);
|
|
|
|
/* sky before edge */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
sky_tile(pa, rl);
|
|
|
|
if (!R.test_break(R.tbh)) {
|
|
if (rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edge_enhance_add(pa, rl->rectf, edgerect);
|
|
}
|
|
|
|
if (rl->passflag & SCE_PASS_VECTOR)
|
|
reset_sky_speed(pa, rl);
|
|
|
|
/* de-premul alpha */
|
|
if (R.r.alphamode & R_ALPHAKEY)
|
|
convert_to_key_alpha(pa, rl);
|
|
|
|
if (edgerect) MEM_freeN(edgerect);
|
|
edgerect= NULL;
|
|
|
|
if (pa->rectmask) {
|
|
MEM_freeN(pa->rectmask);
|
|
pa->rectmask= NULL;
|
|
}
|
|
}
|
|
|
|
/* display active layer */
|
|
rr->renrect.ymin=rr->renrect.ymax = 0;
|
|
rr->renlay= render_get_active_layer(&R, rr);
|
|
|
|
MEM_freeN(pa->recto); pa->recto= NULL;
|
|
MEM_freeN(pa->rectp); pa->rectp= NULL;
|
|
MEM_freeN(pa->rectz); pa->rectz= NULL;
|
|
}
|
|
|
|
/* SSS preprocess tile render, fully threadable */
|
|
typedef struct ZBufSSSHandle {
|
|
RenderPart *pa;
|
|
ListBase psmlist;
|
|
int totps;
|
|
} ZBufSSSHandle;
|
|
|
|
static void addps_sss(void *cb_handle, int obi, int facenr, int x, int y, int z)
|
|
{
|
|
ZBufSSSHandle *handle = cb_handle;
|
|
RenderPart *pa= handle->pa;
|
|
|
|
/* extra border for filter gives double samples on part edges,
|
|
* don't use those */
|
|
if (x<pa->crop || x>=pa->rectx-pa->crop)
|
|
return;
|
|
if (y<pa->crop || y>=pa->recty-pa->crop)
|
|
return;
|
|
|
|
if (pa->rectall) {
|
|
intptr_t *rs= pa->rectall + pa->rectx*y + x;
|
|
|
|
addps(&handle->psmlist, rs, obi, facenr, z, 0, 0);
|
|
handle->totps++;
|
|
}
|
|
if (pa->rectz) {
|
|
int *rz= pa->rectz + pa->rectx*y + x;
|
|
int *rp= pa->rectp + pa->rectx*y + x;
|
|
int *ro= pa->recto + pa->rectx*y + x;
|
|
|
|
if (z < *rz) {
|
|
if (*rp == 0)
|
|
handle->totps++;
|
|
*rz= z;
|
|
*rp= facenr;
|
|
*ro= obi;
|
|
}
|
|
}
|
|
if (pa->rectbackz) {
|
|
int *rz= pa->rectbackz + pa->rectx*y + x;
|
|
int *rp= pa->rectbackp + pa->rectx*y + x;
|
|
int *ro= pa->rectbacko + pa->rectx*y + x;
|
|
|
|
if (z >= *rz) {
|
|
if (*rp == 0)
|
|
handle->totps++;
|
|
*rz= z;
|
|
*rp= facenr;
|
|
*ro= obi;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRen *obi, VlakRen *vlr, int quad, float x, float y, float z, float *co, float color[3], float *area)
|
|
{
|
|
ShadeInput *shi= ssamp->shi;
|
|
ShadeResult shr;
|
|
float /* texfac,*/ /* UNUSED */ orthoarea, nor[3], alpha, sx, sy;
|
|
|
|
/* cache for shadow */
|
|
shi->samplenr= R.shadowsamplenr[shi->thread]++;
|
|
|
|
if (quad)
|
|
shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3);
|
|
else
|
|
shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
|
|
|
|
/* center pixel */
|
|
sx = x + 0.5f;
|
|
sy = y + 0.5f;
|
|
|
|
/* we estimate the area here using shi->dxco and shi->dyco. we need to
|
|
* enabled shi->osatex these are filled. we compute two areas, one with
|
|
* the normal pointed at the camera and one with the original normal, and
|
|
* then clamp to avoid a too large contribution from a single pixel */
|
|
shi->osatex= 1;
|
|
|
|
copy_v3_v3(nor, shi->facenor);
|
|
calc_view_vector(shi->facenor, sx, sy);
|
|
normalize_v3(shi->facenor);
|
|
shade_input_set_viewco(shi, x, y, sx, sy, z);
|
|
orthoarea= len_v3(shi->dxco)*len_v3(shi->dyco);
|
|
|
|
copy_v3_v3(shi->facenor, nor);
|
|
shade_input_set_viewco(shi, x, y, sx, sy, z);
|
|
*area = min_ff(len_v3(shi->dxco) * len_v3(shi->dyco), 2.0f * orthoarea);
|
|
|
|
shade_input_set_uv(shi);
|
|
shade_input_set_normals(shi);
|
|
|
|
/* we don't want flipped normals, they screw up back scattering */
|
|
if (shi->flippednor)
|
|
shade_input_flip_normals(shi);
|
|
|
|
/* not a pretty solution, but fixes common cases */
|
|
if (shi->obr->ob && shi->obr->ob->transflag & OB_NEG_SCALE) {
|
|
negate_v3(shi->vn);
|
|
negate_v3(shi->vno);
|
|
negate_v3(shi->nmapnorm);
|
|
}
|
|
|
|
/* if nodetree, use the material that we are currently preprocessing
|
|
* instead of the node material */
|
|
if (shi->mat->nodetree && shi->mat->use_nodes)
|
|
shi->mat= mat;
|
|
|
|
/* init material vars */
|
|
shade_input_init_material(shi);
|
|
|
|
/* render */
|
|
shade_input_set_shade_texco(shi);
|
|
|
|
shade_samples_do_AO(ssamp);
|
|
shade_material_loop(shi, &shr);
|
|
|
|
copy_v3_v3(co, shi->co);
|
|
copy_v3_v3(color, shr.combined);
|
|
|
|
/* texture blending */
|
|
/* texfac= shi->mat->sss_texfac; */ /* UNUSED */
|
|
|
|
alpha= shr.combined[3];
|
|
*area *= alpha;
|
|
}
|
|
|
|
static void zbufshade_sss_free(RenderPart *pa)
|
|
{
|
|
#if 0
|
|
MEM_freeN(pa->rectall); pa->rectall= NULL;
|
|
freeps(&handle.psmlist);
|
|
#else
|
|
MEM_freeN(pa->rectz); pa->rectz= NULL;
|
|
MEM_freeN(pa->rectp); pa->rectp= NULL;
|
|
MEM_freeN(pa->recto); pa->recto= NULL;
|
|
MEM_freeN(pa->rectbackz); pa->rectbackz= NULL;
|
|
MEM_freeN(pa->rectbackp); pa->rectbackp= NULL;
|
|
MEM_freeN(pa->rectbacko); pa->rectbacko= NULL;
|
|
#endif
|
|
}
|
|
|
|
void zbufshade_sss_tile(RenderPart *pa)
|
|
{
|
|
Render *re= &R;
|
|
ShadeSample ssamp;
|
|
ZBufSSSHandle handle;
|
|
RenderResult *rr= pa->result;
|
|
RenderLayer *rl;
|
|
VlakRen *vlr;
|
|
Material *mat= re->sss_mat;
|
|
float (*co)[3], (*color)[3], *area, *fcol;
|
|
int x, y, seed, quad, totpoint, display = !(re->r.scemode & R_PREVIEWBUTS);
|
|
int *ro, *rz, *rp, *rbo, *rbz, *rbp, lay;
|
|
#if 0
|
|
PixStr *ps;
|
|
intptr_t *rs;
|
|
int z;
|
|
#endif
|
|
|
|
/* setup pixelstr list and buffer for zbuffering */
|
|
handle.pa= pa;
|
|
handle.totps= 0;
|
|
|
|
#if 0
|
|
handle.psmlist.first= handle.psmlist.last= NULL;
|
|
addpsmain(&handle.psmlist);
|
|
|
|
pa->rectall= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "rectall");
|
|
#else
|
|
pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
|
|
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
|
|
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
|
|
pa->rectbacko= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbacko");
|
|
pa->rectbackp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackp");
|
|
pa->rectbackz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackz");
|
|
#endif
|
|
|
|
/* setup shade sample with correct passes */
|
|
memset(&ssamp, 0, sizeof(ssamp));
|
|
shade_sample_initialize(&ssamp, pa, rr->layers.first);
|
|
ssamp.tot= 1;
|
|
|
|
for (rl=rr->layers.first; rl; rl=rl->next) {
|
|
ssamp.shi[0].lay |= rl->lay;
|
|
ssamp.shi[0].layflag |= rl->layflag;
|
|
ssamp.shi[0].passflag |= rl->passflag;
|
|
ssamp.shi[0].combinedflag |= ~rl->pass_xor;
|
|
}
|
|
|
|
rl= rr->layers.first;
|
|
ssamp.shi[0].passflag |= SCE_PASS_RGBA|SCE_PASS_COMBINED;
|
|
ssamp.shi[0].combinedflag &= ~(SCE_PASS_SPEC);
|
|
ssamp.shi[0].mat_override= NULL;
|
|
ssamp.shi[0].light_override= NULL;
|
|
lay= ssamp.shi[0].lay;
|
|
|
|
/* create the pixelstrs to be used later */
|
|
zbuffer_sss(pa, lay, &handle, addps_sss);
|
|
|
|
if (handle.totps==0) {
|
|
zbufshade_sss_free(pa);
|
|
return;
|
|
}
|
|
|
|
fcol= rl->rectf;
|
|
|
|
co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo");
|
|
color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor");
|
|
area= MEM_mallocN(sizeof(float)*handle.totps, "SSSArea");
|
|
|
|
#if 0
|
|
/* create ISB (does not work currently!) */
|
|
if (re->r.mode & R_SHADOW)
|
|
ISB_create(pa, NULL);
|
|
#endif
|
|
|
|
if (display) {
|
|
/* initialize scanline updates for main thread */
|
|
rr->renrect.ymin = 0;
|
|
rr->renlay= rl;
|
|
}
|
|
|
|
seed= pa->rectx*pa->disprect.ymin;
|
|
#if 0
|
|
rs= pa->rectall;
|
|
#else
|
|
rz= pa->rectz;
|
|
rp= pa->rectp;
|
|
ro= pa->recto;
|
|
rbz= pa->rectbackz;
|
|
rbp= pa->rectbackp;
|
|
rbo= pa->rectbacko;
|
|
#endif
|
|
totpoint= 0;
|
|
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, fcol+=4) {
|
|
/* per pixel fixed seed */
|
|
BLI_thread_srandom(pa->thread, seed++);
|
|
|
|
#if 0
|
|
if (rs) {
|
|
/* for each sample in this pixel, shade it */
|
|
for (ps=(PixStr*)*rs; ps; ps=ps->next) {
|
|
ObjectInstanceRen *obi= &re->objectinstance[ps->obi];
|
|
ObjectRen *obr= obi->obr;
|
|
vlr= RE_findOrAddVlak(obr, (ps->facenr-1) & RE_QUAD_MASK);
|
|
quad= (ps->facenr & RE_QUAD_OFFS);
|
|
z= ps->z;
|
|
|
|
shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, z,
|
|
co[totpoint], color[totpoint], &area[totpoint]);
|
|
|
|
totpoint++;
|
|
|
|
add_v3_v3(fcol, color);
|
|
fcol[3]= 1.0f;
|
|
}
|
|
|
|
rs++;
|
|
}
|
|
#else
|
|
if (rp) {
|
|
if (*rp != 0) {
|
|
ObjectInstanceRen *obi= &re->objectinstance[*ro];
|
|
ObjectRen *obr= obi->obr;
|
|
|
|
/* shade front */
|
|
vlr= RE_findOrAddVlak(obr, (*rp-1) & RE_QUAD_MASK);
|
|
quad= ((*rp) & RE_QUAD_OFFS);
|
|
|
|
shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rz,
|
|
co[totpoint], color[totpoint], &area[totpoint]);
|
|
|
|
add_v3_v3(fcol, color[totpoint]);
|
|
fcol[3]= 1.0f;
|
|
totpoint++;
|
|
}
|
|
|
|
rp++; rz++; ro++;
|
|
}
|
|
|
|
if (rbp) {
|
|
if (*rbp != 0 && !(*rbp == *(rp-1) && *rbo == *(ro-1))) {
|
|
ObjectInstanceRen *obi= &re->objectinstance[*rbo];
|
|
ObjectRen *obr= obi->obr;
|
|
|
|
/* shade back */
|
|
vlr= RE_findOrAddVlak(obr, (*rbp-1) & RE_QUAD_MASK);
|
|
quad= ((*rbp) & RE_QUAD_OFFS);
|
|
|
|
shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rbz,
|
|
co[totpoint], color[totpoint], &area[totpoint]);
|
|
|
|
/* to indicate this is a back sample */
|
|
area[totpoint]= -area[totpoint];
|
|
|
|
add_v3_v3(fcol, color[totpoint]);
|
|
fcol[3]= 1.0f;
|
|
totpoint++;
|
|
}
|
|
|
|
rbz++; rbp++; rbo++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (y&1)
|
|
if (re->test_break(re->tbh)) break;
|
|
}
|
|
|
|
/* note: after adding we do not free these arrays, sss keeps them */
|
|
if (totpoint > 0) {
|
|
sss_add_points(re, co, color, area, totpoint);
|
|
}
|
|
else {
|
|
MEM_freeN(co);
|
|
MEM_freeN(color);
|
|
MEM_freeN(area);
|
|
}
|
|
|
|
#if 0
|
|
if (re->r.mode & R_SHADOW)
|
|
ISB_free(pa);
|
|
#endif
|
|
|
|
if (display) {
|
|
/* display active layer */
|
|
rr->renrect.ymin=rr->renrect.ymax = 0;
|
|
rr->renlay= render_get_active_layer(&R, rr);
|
|
}
|
|
|
|
zbufshade_sss_free(pa);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har) /* postprocess version */
|
|
{
|
|
float dist, xsq, ysq, xn, yn, colf[4], *rectft, *rtf;
|
|
float haloxs, haloys;
|
|
int minx, maxx, miny, maxy, x, y;
|
|
|
|
/* calculate the disprect mapped coordinate for halo. note: rectx is disprect corrected */
|
|
haloxs= har->xs - R.disprect.xmin;
|
|
haloys= har->ys - R.disprect.ymin;
|
|
|
|
har->miny= miny= haloys - har->rad/R.ycor;
|
|
har->maxy= maxy= haloys + har->rad/R.ycor;
|
|
|
|
if (maxy < 0) {
|
|
/* pass */
|
|
}
|
|
else if (rr->recty < miny) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
minx = floor(haloxs - har->rad);
|
|
maxx = ceil(haloxs + har->rad);
|
|
|
|
if (maxx < 0) {
|
|
/* pass */
|
|
}
|
|
else if (rr->rectx < minx) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
if (minx<0) minx= 0;
|
|
if (maxx>=rr->rectx) maxx= rr->rectx-1;
|
|
if (miny<0) miny= 0;
|
|
if (maxy>rr->recty) maxy= rr->recty;
|
|
|
|
rectft= rectf+ 4*rr->rectx*miny;
|
|
|
|
for (y=miny; y<maxy; y++) {
|
|
|
|
rtf= rectft+4*minx;
|
|
|
|
yn= (y - haloys)*R.ycor;
|
|
ysq= yn*yn;
|
|
|
|
for (x=minx; x<=maxx; x++) {
|
|
xn= x - haloxs;
|
|
xsq= xn*xn;
|
|
dist= xsq+ysq;
|
|
if (dist<har->radsq) {
|
|
|
|
if (shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec))
|
|
addalphaAddfacFloat(rtf, colf, har->add);
|
|
}
|
|
rtf+=4;
|
|
}
|
|
|
|
rectft+= 4*rr->rectx;
|
|
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static void renderflare(RenderResult *rr, float *rectf, 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.0f! */
|
|
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_post(rr, rectf, 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*fabsf(alfa*visifac*rc[3]);
|
|
fla.hard= 20.0f + fabsf(70.0f*rc[7]);
|
|
fla.tex= 0;
|
|
|
|
type= (int)(fabs(3.9f*rc[6]));
|
|
|
|
fla.rad= ma->subsize*sqrtf(fabs(2.0f*har->rad*rc[4]));
|
|
|
|
if (type==3) {
|
|
fla.rad*= 3.0f;
|
|
fla.rad+= R.rectx/10;
|
|
}
|
|
|
|
fla.radsq= fla.rad*fla.rad;
|
|
|
|
vec[0]= 1.4f*rc[5]*(har->xs-R.winx/2);
|
|
vec[1]= 1.4f*rc[5]*(har->ys-R.winy/2);
|
|
vec[2]= 32.0f*sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + 1.0f);
|
|
|
|
fla.xs= R.winx/2 + vec[0] + (1.2f+rc[8])*R.rectx*vec[0]/vec[2];
|
|
fla.ys= R.winy/2 + vec[1] + (1.2f+rc[8])*R.rectx*vec[1]/vec[2];
|
|
|
|
if (R.flag & R_SEC_FIELD) {
|
|
if (R.r.mode & R_ODDFIELD) fla.ys += 0.5f;
|
|
else fla.ys -= 0.5f;
|
|
}
|
|
if (type & 1) fla.type= HA_FLARECIRC;
|
|
else fla.type= 0;
|
|
renderhalo_post(rr, rectf, &fla);
|
|
|
|
fla.alfa*= 0.5f;
|
|
if (type & 2) fla.type= HA_FLARECIRC;
|
|
else fla.type= 0;
|
|
renderhalo_post(rr, rectf, &fla);
|
|
|
|
rc+= 7;
|
|
}
|
|
}
|
|
|
|
/* needs recode... integrate this better! */
|
|
void add_halo_flare(Render *re)
|
|
{
|
|
RenderResult *rr= re->result;
|
|
RenderLayer *rl;
|
|
HaloRen *har;
|
|
int a, mode, do_draw = FALSE;
|
|
|
|
/* for now, we get the first renderlayer in list with halos set */
|
|
for (rl= rr->layers.first; rl; rl= rl->next)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
break;
|
|
|
|
if (rl==NULL || rl->rectf==NULL)
|
|
return;
|
|
|
|
mode= R.r.mode;
|
|
R.r.mode &= ~R_PANORAMA;
|
|
|
|
project_renderdata(&R, projectverto, 0, 0, 0);
|
|
|
|
for (a=0; a<R.tothalo; a++) {
|
|
har= R.sortedhalos[a];
|
|
|
|
if (har->flarec) {
|
|
do_draw = TRUE;
|
|
renderflare(rr, rl->rectf, har);
|
|
}
|
|
}
|
|
|
|
if (do_draw) {
|
|
/* weak... the display callback wants an active renderlayer pointer... */
|
|
rr->renlay= rl;
|
|
re->display_draw(re->ddh, rr, NULL);
|
|
}
|
|
|
|
R.r.mode= mode;
|
|
}
|
|
|
|
/* ************************* bake ************************ */
|
|
|
|
|
|
typedef struct BakeShade {
|
|
ShadeSample ssamp;
|
|
ObjectInstanceRen *obi;
|
|
VlakRen *vlr;
|
|
|
|
ZSpan *zspan;
|
|
Image *ima;
|
|
ImBuf *ibuf;
|
|
|
|
int rectx, recty, quad, type, vdone, ready;
|
|
|
|
float dir[3];
|
|
Object *actob;
|
|
|
|
unsigned int *rect;
|
|
float *rect_float;
|
|
|
|
int use_mask;
|
|
char *rect_mask; /* bake pixel mask */
|
|
|
|
float dxco[3], dyco[3];
|
|
|
|
short *do_update;
|
|
|
|
struct ColorSpace *rect_colorspace;
|
|
} BakeShade;
|
|
|
|
static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInput *shi, int quad, int UNUSED(isect), int x, int y, float u, float v)
|
|
{
|
|
if (quad)
|
|
shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3);
|
|
else
|
|
shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
|
|
|
|
/* cache for shadow */
|
|
shi->samplenr= R.shadowsamplenr[shi->thread]++;
|
|
|
|
shi->mask= 0xFFFF; /* all samples */
|
|
|
|
shi->u= -u;
|
|
shi->v= -v;
|
|
shi->xs= x;
|
|
shi->ys= y;
|
|
|
|
shade_input_set_uv(shi);
|
|
shade_input_set_normals(shi);
|
|
|
|
/* no normal flip */
|
|
if (shi->flippednor)
|
|
shade_input_flip_normals(shi);
|
|
|
|
/* set up view vector to look right at the surface (note that the normal
|
|
* is negated in the renderer so it does not need to be done here) */
|
|
shi->view[0]= shi->vn[0];
|
|
shi->view[1]= shi->vn[1];
|
|
shi->view[2]= shi->vn[2];
|
|
}
|
|
|
|
static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(quad), int x, int y, float UNUSED(u), float UNUSED(v), float *tvn, float *ttang)
|
|
{
|
|
BakeShade *bs= handle;
|
|
ShadeSample *ssamp= &bs->ssamp;
|
|
ShadeResult shr;
|
|
VlakRen *vlr= shi->vlr;
|
|
|
|
shade_input_init_material(shi);
|
|
|
|
if (bs->type==RE_BAKE_AO) {
|
|
ambient_occlusion(shi);
|
|
|
|
if (R.r.bake_flag & R_BAKE_NORMALIZE) {
|
|
copy_v3_v3(shr.combined, shi->ao);
|
|
}
|
|
else {
|
|
zero_v3(shr.combined);
|
|
environment_lighting_apply(shi, &shr);
|
|
}
|
|
}
|
|
else {
|
|
if (bs->type==RE_BAKE_SHADOW) /* Why do shadows set the color anyhow?, ignore material color for baking */
|
|
shi->r = shi->g = shi->b = 1.0f;
|
|
|
|
shade_input_set_shade_texco(shi);
|
|
|
|
/* only do AO for a full bake (and obviously AO bakes)
|
|
* AO for light bakes is a leftover and might not be needed */
|
|
if ( ELEM3(bs->type, RE_BAKE_ALL, RE_BAKE_AO, RE_BAKE_LIGHT))
|
|
shade_samples_do_AO(ssamp);
|
|
|
|
if (shi->mat->nodetree && shi->mat->use_nodes) {
|
|
ntreeShaderExecTree(shi->mat->nodetree, shi, &shr);
|
|
shi->mat= vlr->mat; /* shi->mat is being set in nodetree */
|
|
}
|
|
else
|
|
shade_material_loop(shi, &shr);
|
|
|
|
if (bs->type==RE_BAKE_NORMALS) {
|
|
float nor[3];
|
|
|
|
copy_v3_v3(nor, shi->vn);
|
|
|
|
if (R.r.bake_normal_space == R_BAKE_SPACE_CAMERA) {
|
|
/* pass */
|
|
}
|
|
else if (R.r.bake_normal_space == R_BAKE_SPACE_TANGENT) {
|
|
float mat[3][3], imat[3][3];
|
|
|
|
/* bitangent */
|
|
if (tvn && ttang) {
|
|
copy_v3_v3(mat[0], ttang);
|
|
cross_v3_v3v3(mat[1], tvn, ttang);
|
|
mul_v3_fl(mat[1], ttang[3]);
|
|
copy_v3_v3(mat[2], tvn);
|
|
}
|
|
else {
|
|
copy_v3_v3(mat[0], shi->nmaptang);
|
|
cross_v3_v3v3(mat[1], shi->nmapnorm, shi->nmaptang);
|
|
mul_v3_fl(mat[1], shi->nmaptang[3]);
|
|
copy_v3_v3(mat[2], shi->nmapnorm);
|
|
}
|
|
|
|
invert_m3_m3(imat, mat);
|
|
mul_m3_v3(imat, nor);
|
|
}
|
|
else if (R.r.bake_normal_space == R_BAKE_SPACE_OBJECT)
|
|
mul_mat3_m4_v3(ob->imat_ren, nor); /* ob->imat_ren includes viewinv! */
|
|
else if (R.r.bake_normal_space == R_BAKE_SPACE_WORLD)
|
|
mul_mat3_m4_v3(R.viewinv, nor);
|
|
|
|
normalize_v3(nor); /* in case object has scaling */
|
|
|
|
/* The invert of the red channel is to make
|
|
* the normal map compliant with the outside world.
|
|
* It needs to be done because in Blender
|
|
* the normal used in the renderer points inward. It is generated
|
|
* this way in calc_vertexnormals(). Should this ever change
|
|
* this negate must be removed. */
|
|
shr.combined[0]= (-nor[0])/2.0f + 0.5f;
|
|
shr.combined[1]= nor[1]/2.0f + 0.5f;
|
|
shr.combined[2]= nor[2]/2.0f + 0.5f;
|
|
}
|
|
else if (bs->type==RE_BAKE_TEXTURE) {
|
|
shr.combined[0]= shi->r;
|
|
shr.combined[1]= shi->g;
|
|
shr.combined[2]= shi->b;
|
|
shr.alpha = shi->alpha;
|
|
}
|
|
else if (bs->type==RE_BAKE_SHADOW) {
|
|
copy_v3_v3(shr.combined, shr.shad);
|
|
shr.alpha = shi->alpha;
|
|
}
|
|
else if (bs->type==RE_BAKE_SPEC_COLOR) {
|
|
shr.combined[0]= shi->specr;
|
|
shr.combined[1]= shi->specg;
|
|
shr.combined[2]= shi->specb;
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type==RE_BAKE_SPEC_INTENSITY) {
|
|
shr.combined[0]=
|
|
shr.combined[1]=
|
|
shr.combined[2]= shi->spec;
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type==RE_BAKE_MIRROR_COLOR) {
|
|
shr.combined[0]= shi->mirr;
|
|
shr.combined[1]= shi->mirg;
|
|
shr.combined[2]= shi->mirb;
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type==RE_BAKE_MIRROR_INTENSITY) {
|
|
shr.combined[0]=
|
|
shr.combined[1]=
|
|
shr.combined[2]= shi->ray_mirror;
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type==RE_BAKE_ALPHA) {
|
|
shr.combined[0]=
|
|
shr.combined[1]=
|
|
shr.combined[2]= shi->alpha;
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type==RE_BAKE_EMIT) {
|
|
shr.combined[0]=
|
|
shr.combined[1]=
|
|
shr.combined[2]= shi->emit;
|
|
shr.alpha = 1.0f;
|
|
}
|
|
}
|
|
|
|
if (bs->rect_float) {
|
|
float *col= bs->rect_float + 4*(bs->rectx*y + x);
|
|
copy_v3_v3(col, shr.combined);
|
|
if (bs->type==RE_BAKE_ALL || bs->type==RE_BAKE_TEXTURE) {
|
|
col[3]= shr.alpha;
|
|
}
|
|
else {
|
|
col[3]= 1.0;
|
|
}
|
|
}
|
|
else {
|
|
unsigned char *col= (unsigned char *)(bs->rect + bs->rectx*y + x);
|
|
|
|
if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) {
|
|
float rgb[3];
|
|
|
|
copy_v3_v3(rgb, shr.combined);
|
|
if (R.scene_color_manage)
|
|
IMB_colormanagement_scene_linear_to_colorspace_v3(rgb, bs->rect_colorspace);
|
|
rgb_float_to_uchar(col, rgb);
|
|
}
|
|
else {
|
|
rgb_float_to_uchar(col, shr.combined);
|
|
}
|
|
|
|
if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) {
|
|
col[3]= FTOCHAR(shr.alpha);
|
|
}
|
|
else {
|
|
col[3]= 255;
|
|
}
|
|
}
|
|
|
|
if (bs->rect_mask) {
|
|
bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED;
|
|
}
|
|
}
|
|
|
|
static void bake_displacement(void *handle, ShadeInput *UNUSED(shi), float dist, int x, int y)
|
|
{
|
|
BakeShade *bs= handle;
|
|
float disp;
|
|
|
|
if (R.r.bake_flag & R_BAKE_NORMALIZE && R.r.bake_maxdist) {
|
|
disp = (dist+R.r.bake_maxdist) / (R.r.bake_maxdist*2); /* alter the range from [-bake_maxdist, bake_maxdist] to [0, 1]*/
|
|
}
|
|
else {
|
|
disp = 0.5f + dist; /* alter the range from [-0.5,0.5] to [0,1]*/
|
|
}
|
|
|
|
if (bs->rect_float) {
|
|
float *col= bs->rect_float + 4*(bs->rectx*y + x);
|
|
col[0] = col[1] = col[2] = disp;
|
|
col[3]= 1.0f;
|
|
}
|
|
else {
|
|
char *col= (char *)(bs->rect + bs->rectx*y + x);
|
|
col[0] = col[1] = col[2] = FTOCHAR(disp);
|
|
col[3]= 255;
|
|
}
|
|
if (bs->rect_mask) {
|
|
bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED;
|
|
}
|
|
}
|
|
|
|
static int bake_intersect_tree(RayObject* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist)
|
|
{
|
|
float maxdist;
|
|
int hit;
|
|
|
|
/* might be useful to make a user setting for maxsize*/
|
|
if (R.r.bake_maxdist > 0.0f)
|
|
maxdist= R.r.bake_maxdist;
|
|
else
|
|
maxdist= RE_RAYTRACE_MAXDIST + R.r.bake_biasdist;
|
|
|
|
/* 'dir' is always normalized */
|
|
madd_v3_v3v3fl(isect->start, start, dir, -R.r.bake_biasdist);
|
|
|
|
mul_v3_v3fl(isect->dir, dir, sign);
|
|
|
|
isect->dist = maxdist;
|
|
|
|
hit = RE_rayobject_raycast(raytree, isect);
|
|
if (hit) {
|
|
madd_v3_v3v3fl(hitco, isect->start, isect->dir, isect->dist);
|
|
|
|
*dist= isect->dist;
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
static void bake_set_vlr_dxyco(BakeShade *bs, float *uv1, float *uv2, float *uv3)
|
|
{
|
|
VlakRen *vlr= bs->vlr;
|
|
float A, d1, d2, d3, *v1, *v2, *v3;
|
|
|
|
if (bs->quad) {
|
|
v1= vlr->v1->co;
|
|
v2= vlr->v3->co;
|
|
v3= vlr->v4->co;
|
|
}
|
|
else {
|
|
v1= vlr->v1->co;
|
|
v2= vlr->v2->co;
|
|
v3= vlr->v3->co;
|
|
}
|
|
|
|
/* formula derived from barycentric coordinates:
|
|
* (uvArea1*v1 + uvArea2*v2 + uvArea3*v3)/uvArea
|
|
* then taking u and v partial derivatives to get dxco and dyco */
|
|
A= (uv2[0] - uv1[0])*(uv3[1] - uv1[1]) - (uv3[0] - uv1[0])*(uv2[1] - uv1[1]);
|
|
|
|
if (fabsf(A) > FLT_EPSILON) {
|
|
A= 0.5f/A;
|
|
|
|
d1= uv2[1] - uv3[1];
|
|
d2= uv3[1] - uv1[1];
|
|
d3= uv1[1] - uv2[1];
|
|
bs->dxco[0]= (v1[0]*d1 + v2[0]*d2 + v3[0]*d3)*A;
|
|
bs->dxco[1]= (v1[1]*d1 + v2[1]*d2 + v3[1]*d3)*A;
|
|
bs->dxco[2]= (v1[2]*d1 + v2[2]*d2 + v3[2]*d3)*A;
|
|
|
|
d1= uv3[0] - uv2[0];
|
|
d2= uv1[0] - uv3[0];
|
|
d3= uv2[0] - uv1[0];
|
|
bs->dyco[0]= (v1[0]*d1 + v2[0]*d2 + v3[0]*d3)*A;
|
|
bs->dyco[1]= (v1[1]*d1 + v2[1]*d2 + v3[1]*d3)*A;
|
|
bs->dyco[2]= (v1[2]*d1 + v2[2]*d2 + v3[2]*d3)*A;
|
|
}
|
|
else {
|
|
bs->dxco[0]= bs->dxco[1]= bs->dxco[2]= 0.0f;
|
|
bs->dyco[0]= bs->dyco[1]= bs->dyco[2]= 0.0f;
|
|
}
|
|
|
|
if (bs->obi->flag & R_TRANSFORMED) {
|
|
mul_m3_v3(bs->obi->nmat, bs->dxco);
|
|
mul_m3_v3(bs->obi->nmat, bs->dyco);
|
|
}
|
|
}
|
|
|
|
static void do_bake_shade(void *handle, int x, int y, float u, float v)
|
|
{
|
|
BakeShade *bs= handle;
|
|
VlakRen *vlr= bs->vlr;
|
|
ObjectInstanceRen *obi= bs->obi;
|
|
Object *ob= obi->obr->ob;
|
|
float l, *v1, *v2, *v3, tvn[3], ttang[4];
|
|
int quad;
|
|
ShadeSample *ssamp= &bs->ssamp;
|
|
ShadeInput *shi= ssamp->shi;
|
|
|
|
/* fast threadsafe break test */
|
|
if (R.test_break(R.tbh))
|
|
return;
|
|
|
|
/* setup render coordinates */
|
|
if (bs->quad) {
|
|
v1= vlr->v1->co;
|
|
v2= vlr->v3->co;
|
|
v3= vlr->v4->co;
|
|
}
|
|
else {
|
|
v1= vlr->v1->co;
|
|
v2= vlr->v2->co;
|
|
v3= vlr->v3->co;
|
|
}
|
|
|
|
l= 1.0f-u-v;
|
|
|
|
/* shrink barycentric coordinates inwards slightly to avoid some issues
|
|
* where baking selected to active might just miss the other face at the
|
|
* near the edge of a face */
|
|
if (bs->actob) {
|
|
const float eps = 1.0f - 1e-4f;
|
|
float invsum;
|
|
|
|
u = (u - 0.5f)*eps + 0.5f;
|
|
v = (v - 0.5f)*eps + 0.5f;
|
|
l = (l - 0.5f)*eps + 0.5f;
|
|
|
|
invsum = 1.0f/(u + v + l);
|
|
|
|
u *= invsum;
|
|
v *= invsum;
|
|
l *= invsum;
|
|
}
|
|
|
|
/* renderco */
|
|
shi->co[0]= l*v3[0]+u*v1[0]+v*v2[0];
|
|
shi->co[1]= l*v3[1]+u*v1[1]+v*v2[1];
|
|
shi->co[2]= l*v3[2]+u*v1[2]+v*v2[2];
|
|
|
|
if (obi->flag & R_TRANSFORMED)
|
|
mul_m4_v3(obi->mat, shi->co);
|
|
|
|
copy_v3_v3(shi->dxco, bs->dxco);
|
|
copy_v3_v3(shi->dyco, bs->dyco);
|
|
|
|
quad= bs->quad;
|
|
bake_set_shade_input(obi, vlr, shi, quad, 0, x, y, u, v);
|
|
|
|
if (bs->type==RE_BAKE_NORMALS && R.r.bake_normal_space==R_BAKE_SPACE_TANGENT) {
|
|
shade_input_set_shade_texco(shi);
|
|
copy_v3_v3(tvn, shi->nmapnorm);
|
|
copy_v4_v4(ttang, shi->nmaptang);
|
|
}
|
|
|
|
/* if we are doing selected to active baking, find point on other face */
|
|
if (bs->actob) {
|
|
Isect isec, minisec;
|
|
float co[3], minco[3], dist, mindist=0.0f;
|
|
int hit, sign, dir=1;
|
|
|
|
/* intersect with ray going forward and backward*/
|
|
hit= 0;
|
|
memset(&minisec, 0, sizeof(minisec));
|
|
minco[0]= minco[1]= minco[2]= 0.0f;
|
|
|
|
copy_v3_v3(bs->dir, shi->vn);
|
|
|
|
for (sign=-1; sign<=1; sign+=2) {
|
|
memset(&isec, 0, sizeof(isec));
|
|
isec.mode= RE_RAY_MIRROR;
|
|
|
|
isec.orig.ob = obi;
|
|
isec.orig.face = vlr;
|
|
isec.userdata= bs->actob;
|
|
isec.check = RE_CHECK_VLR_BAKE;
|
|
isec.skip = RE_SKIP_VLR_NEIGHBOUR;
|
|
|
|
if (bake_intersect_tree(R.raytree, &isec, shi->co, shi->vn, sign, co, &dist)) {
|
|
if (!hit || len_squared_v3v3(shi->co, co) < len_squared_v3v3(shi->co, minco)) {
|
|
minisec= isec;
|
|
mindist= dist;
|
|
copy_v3_v3(minco, co);
|
|
hit= 1;
|
|
dir = sign;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bs->type==RE_BAKE_DISPLACEMENT) {
|
|
if (hit)
|
|
bake_displacement(handle, shi, (dir==-1)? mindist:-mindist, x, y);
|
|
else
|
|
bake_displacement(handle, shi, 0.0f, x, y);
|
|
return;
|
|
}
|
|
|
|
/* if hit, we shade from the new point, otherwise from point one starting face */
|
|
if (hit) {
|
|
obi= (ObjectInstanceRen*)minisec.hit.ob;
|
|
vlr= (VlakRen*)minisec.hit.face;
|
|
quad= (minisec.isect == 2);
|
|
copy_v3_v3(shi->co, minco);
|
|
|
|
u= -minisec.u;
|
|
v= -minisec.v;
|
|
bake_set_shade_input(obi, vlr, shi, quad, 1, x, y, u, v);
|
|
}
|
|
}
|
|
|
|
if (bs->type==RE_BAKE_NORMALS && R.r.bake_normal_space==R_BAKE_SPACE_TANGENT)
|
|
bake_shade(handle, ob, shi, quad, x, y, u, v, tvn, ttang);
|
|
else
|
|
bake_shade(handle, ob, shi, quad, x, y, u, v, 0, 0);
|
|
}
|
|
|
|
static int get_next_bake_face(BakeShade *bs)
|
|
{
|
|
ObjectRen *obr;
|
|
VlakRen *vlr;
|
|
MTFace *tface;
|
|
static int v= 0, vdone = FALSE;
|
|
static ObjectInstanceRen *obi= NULL;
|
|
|
|
if (bs==NULL) {
|
|
vlr= NULL;
|
|
v= vdone = FALSE;
|
|
obi= R.instancetable.first;
|
|
return 0;
|
|
}
|
|
|
|
BLI_lock_thread(LOCK_CUSTOM1);
|
|
|
|
for (; obi; obi=obi->next, v=0) {
|
|
obr= obi->obr;
|
|
|
|
for (; v<obr->totvlak; v++) {
|
|
vlr= RE_findOrAddVlak(obr, v);
|
|
|
|
if ((bs->actob && bs->actob == obr->ob) || (!bs->actob && (obr->ob->flag & SELECT))) {
|
|
tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
|
|
|
|
if (tface && tface->tpage) {
|
|
Image *ima= tface->tpage;
|
|
ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
|
|
const float vec_alpha[4]= {0.0f, 0.0f, 0.0f, 0.0f};
|
|
const float vec_solid[4]= {0.0f, 0.0f, 0.0f, 1.0f};
|
|
|
|
if (ibuf==NULL)
|
|
continue;
|
|
|
|
if (ibuf->rect==NULL && ibuf->rect_float==NULL)
|
|
continue;
|
|
|
|
if (ibuf->rect_float && !(ibuf->channels==0 || ibuf->channels==4))
|
|
continue;
|
|
|
|
if (ima->flag & IMA_USED_FOR_RENDER) {
|
|
ima->id.flag &= ~LIB_DOIT;
|
|
continue;
|
|
}
|
|
|
|
/* find the image for the first time? */
|
|
if (ima->id.flag & LIB_DOIT) {
|
|
ima->id.flag &= ~LIB_DOIT;
|
|
|
|
/* we either fill in float or char, this ensures things go fine */
|
|
if (ibuf->rect_float)
|
|
imb_freerectImBuf(ibuf);
|
|
/* clear image */
|
|
if (R.r.bake_flag & R_BAKE_CLEAR)
|
|
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
|
|
|
|
/* might be read by UI to set active image for display */
|
|
R.bakebuf= ima;
|
|
}
|
|
|
|
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
|
|
|
bs->obi= obi;
|
|
bs->vlr= vlr;
|
|
|
|
bs->vdone++; /* only for error message if nothing was rendered */
|
|
v++;
|
|
|
|
BLI_unlock_thread(LOCK_CUSTOM1);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_unlock_thread(LOCK_CUSTOM1);
|
|
return 0;
|
|
}
|
|
|
|
/* already have tested for tface and ima and zspan */
|
|
static void shade_tface(BakeShade *bs)
|
|
{
|
|
VlakRen *vlr= bs->vlr;
|
|
ObjectInstanceRen *obi= bs->obi;
|
|
ObjectRen *obr= obi->obr;
|
|
MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
|
|
Image *ima= tface->tpage;
|
|
float vec[4][2];
|
|
int a, i1, i2, i3;
|
|
|
|
/* check valid zspan */
|
|
if (ima!=bs->ima) {
|
|
bs->ima= ima;
|
|
bs->ibuf= BKE_image_get_ibuf(ima, NULL);
|
|
/* note, these calls only free/fill contents of zspan struct, not zspan itself */
|
|
zbuf_free_span(bs->zspan);
|
|
zbuf_alloc_span(bs->zspan, bs->ibuf->x, bs->ibuf->y, R.clipcrop);
|
|
}
|
|
|
|
bs->rectx= bs->ibuf->x;
|
|
bs->recty= bs->ibuf->y;
|
|
bs->rect= bs->ibuf->rect;
|
|
bs->rect_colorspace= bs->ibuf->rect_colorspace;
|
|
bs->rect_float= bs->ibuf->rect_float;
|
|
bs->quad= 0;
|
|
|
|
if (bs->use_mask) {
|
|
if (bs->ibuf->userdata==NULL) {
|
|
BLI_lock_thread(LOCK_CUSTOM1);
|
|
if (bs->ibuf->userdata==NULL) /* since the thread was locked, its possible another thread alloced the value */
|
|
bs->ibuf->userdata = (void *)MEM_callocN(sizeof(char)*bs->rectx*bs->recty, "BakeMask");
|
|
bs->rect_mask= (char *)bs->ibuf->userdata;
|
|
BLI_unlock_thread(LOCK_CUSTOM1);
|
|
}
|
|
else {
|
|
bs->rect_mask= (char *)bs->ibuf->userdata;
|
|
}
|
|
}
|
|
|
|
/* get pixel level vertex coordinates */
|
|
for (a=0; a<4; a++) {
|
|
/* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests
|
|
* where a pixel gets in between 2 faces or the middle of a quad,
|
|
* camera aligned quads also have this problem but they are less common.
|
|
* Add a small offset to the UVs, fixes bug #18685 - Campbell */
|
|
vec[a][0]= tface->uv[a][0]*(float)bs->rectx - (0.5f + 0.001f);
|
|
vec[a][1]= tface->uv[a][1]*(float)bs->recty - (0.5f + 0.002f);
|
|
}
|
|
|
|
/* UV indices have to be corrected for possible quad->tria splits */
|
|
i1= 0; i2= 1; i3= 2;
|
|
vlr_set_uv_indices(vlr, &i1, &i2, &i3);
|
|
bake_set_vlr_dxyco(bs, vec[i1], vec[i2], vec[i3]);
|
|
zspan_scanconvert(bs->zspan, bs, vec[i1], vec[i2], vec[i3], do_bake_shade);
|
|
|
|
if (vlr->v4) {
|
|
bs->quad= 1;
|
|
bake_set_vlr_dxyco(bs, vec[0], vec[2], vec[3]);
|
|
zspan_scanconvert(bs->zspan, bs, vec[0], vec[2], vec[3], do_bake_shade);
|
|
}
|
|
}
|
|
|
|
static void *do_bake_thread(void *bs_v)
|
|
{
|
|
BakeShade *bs= bs_v;
|
|
|
|
while (get_next_bake_face(bs)) {
|
|
shade_tface(bs);
|
|
|
|
/* fast threadsafe break test */
|
|
if (R.test_break(R.tbh))
|
|
break;
|
|
|
|
/* access is not threadsafe but since its just true/false probably ok
|
|
* only used for interactive baking */
|
|
if (bs->do_update)
|
|
*bs->do_update= TRUE;
|
|
}
|
|
bs->ready= 1;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void RE_bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
|
|
{
|
|
/* must check before filtering */
|
|
const short is_new_alpha= (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf);
|
|
|
|
/* Margin */
|
|
if (filter) {
|
|
IMB_filter_extend(ibuf, mask, filter);
|
|
}
|
|
|
|
/* if the bake results in new alpha then change the image setting */
|
|
if (is_new_alpha) {
|
|
ibuf->planes= R_IMF_PLANES_RGBA;
|
|
}
|
|
else {
|
|
if (filter && ibuf->planes != R_IMF_PLANES_RGBA) {
|
|
/* clear alpha added by filtering */
|
|
IMB_rectfill_alpha(ibuf, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* using object selection tags, the faces with UV maps get baked */
|
|
/* render should have been setup */
|
|
/* returns 0 if nothing was handled */
|
|
int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_update, float *progress)
|
|
{
|
|
BakeShade *handles;
|
|
ListBase threads;
|
|
Image *ima;
|
|
int a, vdone = FALSE, use_mask = FALSE, result = BAKE_RESULT_OK;
|
|
|
|
re->scene_color_manage = BKE_scene_check_color_management_enabled(re->scene);
|
|
|
|
/* initialize render global */
|
|
R= *re;
|
|
R.bakebuf= NULL;
|
|
|
|
/* initialize static vars */
|
|
get_next_bake_face(NULL);
|
|
|
|
/* do we need a mask? */
|
|
if (re->r.bake_filter)
|
|
use_mask = TRUE;
|
|
|
|
/* baker uses this flag to detect if image was initialized */
|
|
for (ima= G.main->image.first; ima; ima= ima->id.next) {
|
|
ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
|
|
ima->id.flag |= LIB_DOIT;
|
|
ima->flag&= ~IMA_USED_FOR_RENDER;
|
|
if (ibuf) {
|
|
ibuf->userdata = NULL; /* use for masking if needed */
|
|
}
|
|
}
|
|
|
|
BLI_init_threads(&threads, do_bake_thread, re->r.threads);
|
|
|
|
handles= MEM_callocN(sizeof(BakeShade)*re->r.threads, "BakeShade");
|
|
|
|
/* get the threads running */
|
|
for (a=0; a<re->r.threads; a++) {
|
|
/* set defaults in handles */
|
|
handles[a].ssamp.shi[0].lay= re->lay;
|
|
|
|
if (type==RE_BAKE_SHADOW) {
|
|
handles[a].ssamp.shi[0].passflag= SCE_PASS_SHADOW;
|
|
}
|
|
else {
|
|
handles[a].ssamp.shi[0].passflag= SCE_PASS_COMBINED;
|
|
}
|
|
handles[a].ssamp.shi[0].combinedflag= ~(SCE_PASS_SPEC);
|
|
handles[a].ssamp.shi[0].thread= a;
|
|
handles[a].ssamp.tot= 1;
|
|
|
|
handles[a].type= type;
|
|
handles[a].actob= actob;
|
|
handles[a].zspan= MEM_callocN(sizeof(ZSpan), "zspan for bake");
|
|
|
|
handles[a].use_mask = use_mask;
|
|
|
|
handles[a].do_update = do_update; /* use to tell the view to update */
|
|
|
|
BLI_insert_thread(&threads, &handles[a]);
|
|
}
|
|
|
|
/* wait for everything to be done */
|
|
a= 0;
|
|
while (a!=re->r.threads) {
|
|
PIL_sleep_ms(50);
|
|
|
|
/* calculate progress */
|
|
for (vdone = FALSE, a=0; a<re->r.threads; a++)
|
|
vdone+= handles[a].vdone;
|
|
if (progress)
|
|
*progress = (float)(vdone / (float)re->totvlak);
|
|
|
|
for (a=0; a<re->r.threads; a++) {
|
|
if (handles[a].ready==0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* filter and refresh images */
|
|
for (ima= G.main->image.first; ima; ima= ima->id.next) {
|
|
if ((ima->id.flag & LIB_DOIT)==0) {
|
|
ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
|
|
|
|
if (ima->flag & IMA_USED_FOR_RENDER)
|
|
result= BAKE_RESULT_FEEDBACK_LOOP;
|
|
|
|
if (!ibuf)
|
|
continue;
|
|
|
|
RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, re->r.bake_filter);
|
|
|
|
ibuf->userflags |= IB_BITMAPDIRTY;
|
|
}
|
|
}
|
|
|
|
/* calculate return value */
|
|
for (a=0; a<re->r.threads; a++) {
|
|
zbuf_free_span(handles[a].zspan);
|
|
MEM_freeN(handles[a].zspan);
|
|
}
|
|
|
|
MEM_freeN(handles);
|
|
|
|
BLI_end_threads(&threads);
|
|
|
|
if (vdone==0)
|
|
result= BAKE_RESULT_NO_OBJECTS;
|
|
|
|
return result;
|
|
}
|
|
|
|
struct Image *RE_bake_shade_get_image(void)
|
|
{
|
|
return R.bakebuf;
|
|
}
|
|
|