1343 lines
36 KiB
C
1343 lines
36 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.
|
|
*
|
|
* Contributors: 2004/2005/2006 Blender Foundation, full recode
|
|
* Contributors: Vertex color baking, Copyright 2011 AutoCRC
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/render/intern/source/bake.c
|
|
* \ingroup render
|
|
*/
|
|
|
|
|
|
/* system includes */
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* External modules: */
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_rand.h"
|
|
#include "BLI_threads.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "BKE_customdata.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_node.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_library.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_colormanagement.h"
|
|
|
|
/* local include */
|
|
#include "rayintersection.h"
|
|
#include "rayobject.h"
|
|
#include "render_types.h"
|
|
#include "renderdatabase.h"
|
|
#include "shading.h"
|
|
#include "zbuf.h"
|
|
|
|
#include "PIL_time.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;
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
|
|
/* ************************* bake ************************ */
|
|
|
|
|
|
typedef struct BakeShade {
|
|
int thread;
|
|
|
|
ShadeSample ssamp;
|
|
ObjectInstanceRen *obi;
|
|
VlakRen *vlr;
|
|
|
|
ZSpan *zspan;
|
|
Image *ima;
|
|
ImBuf *ibuf;
|
|
|
|
int rectx, recty, quad, type, vdone;
|
|
bool ready;
|
|
|
|
float dir[3];
|
|
Object *actob;
|
|
|
|
/* Output: vertex color or image data. If vcol is not NULL, rect and
|
|
* rect_float should be NULL. */
|
|
MPoly *mpoly;
|
|
MLoop *mloop;
|
|
MLoopCol *vcol;
|
|
|
|
unsigned int *rect;
|
|
float *rect_float;
|
|
|
|
/* displacement buffer used for normalization with unknown maximal distance */
|
|
bool use_displacement_buffer;
|
|
float *displacement_buffer;
|
|
float displacement_min, displacement_max;
|
|
|
|
bool 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 (ELEM(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.
|
|
*
|
|
* there is also a small 1e-5f bias for precision issues. otherwise
|
|
* we randomly get 127 or 128 for neutral colors. we choose 128
|
|
* because it is the convention flat color. * */
|
|
shr.combined[0] = (-nor[0]) / 2.0f + 0.5f + 1e-5f;
|
|
shr.combined[1] = nor[1] / 2.0f + 0.5f + 1e-5f;
|
|
shr.combined[2] = nor[2] / 2.0f + 0.5f + 1e-5f;
|
|
}
|
|
else if (bs->type == RE_BAKE_TEXTURE) {
|
|
copy_v3_v3(shr.combined, &shi->r);
|
|
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) {
|
|
copy_v3_v3(shr.combined, &shi->specr);
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type == RE_BAKE_SPEC_INTENSITY) {
|
|
copy_v3_fl(shr.combined, shi->spec);
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type == RE_BAKE_MIRROR_COLOR) {
|
|
copy_v3_v3(shr.combined, &shi->mirr);
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type == RE_BAKE_MIRROR_INTENSITY) {
|
|
copy_v3_fl(shr.combined, shi->ray_mirror);
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type == RE_BAKE_ALPHA) {
|
|
copy_v3_fl(shr.combined, shi->alpha);
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type == RE_BAKE_EMIT) {
|
|
copy_v3_fl(shr.combined, shi->emit);
|
|
shr.alpha = 1.0f;
|
|
}
|
|
else if (bs->type == RE_BAKE_VERTEX_COLORS) {
|
|
copy_v3_v3(shr.combined, shi->vcol);
|
|
shr.alpha = shi->vcol[3];
|
|
}
|
|
}
|
|
|
|
if (bs->rect_float && !bs->vcol) {
|
|
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 || bs->type == RE_BAKE_VERTEX_COLORS) {
|
|
col[3] = shr.alpha;
|
|
}
|
|
else {
|
|
col[3] = 1.0;
|
|
}
|
|
}
|
|
else {
|
|
/* Target is char (LDR). */
|
|
unsigned char col[4];
|
|
|
|
if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) {
|
|
float rgb[3];
|
|
|
|
copy_v3_v3(rgb, shr.combined);
|
|
if (R.scene_color_manage) {
|
|
/* Vertex colors have no way to specify color space, so they
|
|
* default to sRGB. */
|
|
if (!bs->vcol)
|
|
IMB_colormanagement_scene_linear_to_colorspace_v3(rgb, bs->rect_colorspace);
|
|
else
|
|
linearrgb_to_srgb_v3_v3(rgb, rgb);
|
|
}
|
|
rgb_float_to_uchar(col, rgb);
|
|
}
|
|
else {
|
|
rgb_float_to_uchar(col, shr.combined);
|
|
}
|
|
|
|
if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE, RE_BAKE_VERTEX_COLORS)) {
|
|
col[3] = FTOCHAR(shr.alpha);
|
|
}
|
|
else {
|
|
col[3] = 255;
|
|
}
|
|
|
|
if (bs->vcol) {
|
|
/* Vertex color baking. Vcol has no useful alpha channel (it exists
|
|
* but is used only for vertex painting). */
|
|
bs->vcol->r = col[0];
|
|
bs->vcol->g = col[1];
|
|
bs->vcol->b = col[2];
|
|
}
|
|
else {
|
|
unsigned char *imcol = (unsigned char *)(bs->rect + bs->rectx * y + x);
|
|
copy_v4_v4_uchar(imcol, col);
|
|
}
|
|
|
|
}
|
|
|
|
if (bs->rect_mask) {
|
|
bs->rect_mask[bs->rectx * y + x] = FILTER_MASK_USED;
|
|
}
|
|
|
|
if (bs->do_update) {
|
|
*bs->do_update = true;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
if (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 = dist;
|
|
}
|
|
else {
|
|
disp = 0.5f + dist; /* alter the range from [-0.5,0.5] to [0,1]*/
|
|
}
|
|
|
|
if (bs->displacement_buffer) {
|
|
float *displacement = bs->displacement_buffer + (bs->rectx * y + x);
|
|
*displacement = disp;
|
|
bs->displacement_min = min_ff(bs->displacement_min, disp);
|
|
bs->displacement_max = max_ff(bs->displacement_max, disp);
|
|
}
|
|
|
|
if (bs->rect_float && !bs->vcol) {
|
|
float *col = bs->rect_float + 4 * (bs->rectx * y + x);
|
|
col[0] = col[1] = col[2] = disp;
|
|
col[3] = 1.0f;
|
|
}
|
|
else {
|
|
/* Target is char (LDR). */
|
|
unsigned char col[4];
|
|
col[0] = col[1] = col[2] = FTOCHAR(disp);
|
|
col[3] = 255;
|
|
|
|
if (bs->vcol) {
|
|
/* Vertex color baking. Vcol has no useful alpha channel (it exists
|
|
* but is used only for vertex painting). */
|
|
bs->vcol->r = col[0];
|
|
bs->vcol->g = col[1];
|
|
bs->vcol->b = col[2];
|
|
}
|
|
else {
|
|
unsigned char *imcol = (unsigned char *)(bs->rect + bs->rectx * y + x);
|
|
copy_v4_v4_uchar(imcol, col);
|
|
}
|
|
}
|
|
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];
|
|
|
|
/* avoid self shadow with vertex bake from adjacent faces [#33729] */
|
|
if ((bs->vcol != NULL) && (bs->actob == NULL)) {
|
|
madd_v3_v3fl(shi->co, vlr->n, 0.0001f);
|
|
}
|
|
|
|
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 (ELEM(bs->type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
|
|
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, NULL, NULL);
|
|
}
|
|
|
|
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;
|
|
|
|
/* only allow non instances here */
|
|
if (obr->flag & R_INSTANCEABLE)
|
|
continue;
|
|
|
|
for (; v < obr->totvlak; v++) {
|
|
vlr = RE_findOrAddVlak(obr, v);
|
|
|
|
if ((bs->actob && bs->actob == obr->ob) || (!bs->actob && (obr->ob->flag & SELECT))) {
|
|
if (R.r.bake_flag & R_BAKE_VCOL) {
|
|
/* Gather face data for vertex color bake */
|
|
Mesh *me;
|
|
int *origindex, vcollayer;
|
|
CustomDataLayer *cdl;
|
|
|
|
if (obr->ob->type != OB_MESH)
|
|
continue;
|
|
me = obr->ob->data;
|
|
|
|
origindex = RE_vlakren_get_origindex(obr, vlr, 0);
|
|
if (origindex == NULL)
|
|
continue;
|
|
if (*origindex >= me->totpoly) {
|
|
/* Small hack for Array modifier, which gives false
|
|
* original indices - z0r */
|
|
continue;
|
|
}
|
|
#if 0
|
|
/* Only shade selected faces. */
|
|
if ((me->mface[*origindex].flag & ME_FACE_SEL) == 0)
|
|
continue;
|
|
#endif
|
|
|
|
vcollayer = CustomData_get_render_layer_index(&me->ldata, CD_MLOOPCOL);
|
|
if (vcollayer == -1)
|
|
continue;
|
|
|
|
cdl = &me->ldata.layers[vcollayer];
|
|
bs->mpoly = me->mpoly + *origindex;
|
|
bs->vcol = ((MLoopCol *)cdl->data) + bs->mpoly->loopstart;
|
|
bs->mloop = me->mloop + bs->mpoly->loopstart;
|
|
|
|
/* Tag mesh for reevaluation. */
|
|
me->id.tag |= LIB_TAG_DOIT;
|
|
}
|
|
else {
|
|
Image *ima = NULL;
|
|
ImBuf *ibuf = 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};
|
|
const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f};
|
|
const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f};
|
|
const float disp_alpha[4] = {0.5f, 0.5f, 0.5f, 0.0f};
|
|
const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
|
|
tface = RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
|
|
|
|
if (!tface || !tface->tpage)
|
|
continue;
|
|
|
|
ima = tface->tpage;
|
|
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
|
|
|
if (ibuf == NULL)
|
|
continue;
|
|
|
|
if (ibuf->rect == NULL && ibuf->rect_float == NULL) {
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
continue;
|
|
}
|
|
|
|
if (ibuf->rect_float && !(ibuf->channels == 0 || ibuf->channels == 4)) {
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
continue;
|
|
}
|
|
|
|
if (ima->flag & IMA_USED_FOR_RENDER) {
|
|
ima->id.tag &= ~LIB_TAG_DOIT;
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
continue;
|
|
}
|
|
|
|
/* find the image for the first time? */
|
|
if (ima->id.tag & LIB_TAG_DOIT) {
|
|
ima->id.tag &= ~LIB_TAG_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) {
|
|
if (R.r.bake_mode == RE_BAKE_NORMALS && R.r.bake_normal_space == R_BAKE_SPACE_TANGENT)
|
|
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
|
|
else if (ELEM(R.r.bake_mode, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE))
|
|
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
|
|
else
|
|
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;
|
|
}
|
|
|
|
/* Tag image for redraw. */
|
|
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void bake_single_vertex(BakeShade *bs, VertRen *vert, float u, float v)
|
|
{
|
|
int *origindex, i;
|
|
MLoopCol *basevcol;
|
|
MLoop *mloop;
|
|
|
|
/* per vertex fixed seed */
|
|
BLI_thread_srandom(bs->thread, vert->index);
|
|
|
|
origindex = RE_vertren_get_origindex(bs->obi->obr, vert, 0);
|
|
if (!origindex || *origindex == ORIGINDEX_NONE)
|
|
return;
|
|
|
|
/* Search for matching vertex index and apply shading. */
|
|
for (i = 0; i < bs->mpoly->totloop; i++) {
|
|
mloop = bs->mloop + i;
|
|
if (mloop->v != *origindex)
|
|
continue;
|
|
basevcol = bs->vcol;
|
|
bs->vcol = basevcol + i;
|
|
do_bake_shade(bs, 0, 0, u, v);
|
|
bs->vcol = basevcol;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Bake all vertices of a face. Actually, this still works on a face-by-face
|
|
* basis, and each vertex on each face is shaded. Vertex colors are a property
|
|
* of loops, not vertices. */
|
|
static void shade_verts(BakeShade *bs)
|
|
{
|
|
VlakRen *vlr = bs->vlr;
|
|
|
|
/* Disable baking to image; write to vcol instead. vcol pointer is set in
|
|
* bake_single_vertex. */
|
|
bs->ima = NULL;
|
|
bs->rect = NULL;
|
|
bs->rect_float = NULL;
|
|
bs->displacement_buffer = NULL;
|
|
bs->displacement_min = FLT_MAX;
|
|
bs->displacement_max = -FLT_MAX;
|
|
|
|
bs->quad = 0;
|
|
|
|
/* No anti-aliasing for vertices. */
|
|
zero_v3(bs->dxco);
|
|
zero_v3(bs->dyco);
|
|
|
|
/* Shade each vertex of the face. u and v are barycentric coordinates; since
|
|
* we're only interested in vertices, these will be 0 or 1. */
|
|
if ((vlr->flag & R_FACE_SPLIT) == 0) {
|
|
/* Processing triangle face, whole quad, or first half of split quad. */
|
|
|
|
bake_single_vertex(bs, bs->vlr->v1, 1.0f, 0.0f);
|
|
bake_single_vertex(bs, bs->vlr->v2, 0.0f, 1.0f);
|
|
bake_single_vertex(bs, bs->vlr->v3, 0.0f, 0.0f);
|
|
|
|
if (vlr->v4) {
|
|
bs->quad = 1;
|
|
bake_single_vertex(bs, bs->vlr->v4, 0.0f, 0.0f);
|
|
}
|
|
}
|
|
else {
|
|
/* Processing second half of split quad. Only one vertex to go. */
|
|
if (vlr->flag & R_DIVIDE_24) {
|
|
bake_single_vertex(bs, bs->vlr->v2, 0.0f, 1.0f);
|
|
}
|
|
else {
|
|
bake_single_vertex(bs, bs->vlr->v3, 0.0f, 0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* per face fixed seed */
|
|
BLI_thread_srandom(bs->thread, vlr->index);
|
|
|
|
/* check valid zspan */
|
|
if (ima != bs->ima) {
|
|
BKE_image_release_ibuf(bs->ima, bs->ibuf, NULL);
|
|
|
|
bs->ima = ima;
|
|
bs->ibuf = BKE_image_acquire_ibuf(ima, NULL, 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->vcol = NULL;
|
|
bs->quad = 0;
|
|
bs->rect_mask = NULL;
|
|
bs->displacement_buffer = NULL;
|
|
|
|
if (bs->use_mask || bs->use_displacement_buffer) {
|
|
BakeImBufuserData *userdata = bs->ibuf->userdata;
|
|
if (userdata == NULL) {
|
|
BLI_lock_thread(LOCK_CUSTOM1);
|
|
userdata = bs->ibuf->userdata;
|
|
if (userdata == NULL) /* since the thread was locked, its possible another thread alloced the value */
|
|
userdata = MEM_callocN(sizeof(BakeImBufuserData), "BakeImBufuserData");
|
|
|
|
if (bs->use_mask) {
|
|
if (userdata->mask_buffer == NULL) {
|
|
userdata->mask_buffer = MEM_callocN(sizeof(char) * bs->rectx * bs->recty, "BakeMask");
|
|
}
|
|
}
|
|
|
|
if (bs->use_displacement_buffer) {
|
|
if (userdata->displacement_buffer == NULL) {
|
|
userdata->displacement_buffer = MEM_callocN(sizeof(float) * bs->rectx * bs->recty, "BakeDisp");
|
|
}
|
|
}
|
|
|
|
bs->ibuf->userdata = userdata;
|
|
|
|
BLI_unlock_thread(LOCK_CUSTOM1);
|
|
}
|
|
|
|
bs->rect_mask = userdata->mask_buffer;
|
|
bs->displacement_buffer = userdata->displacement_buffer;
|
|
}
|
|
|
|
/* 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)) {
|
|
if (R.r.bake_flag & R_BAKE_VCOL) {
|
|
shade_verts(bs);
|
|
}
|
|
else {
|
|
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 = true;
|
|
|
|
BKE_image_release_ibuf(bs->ima, bs->ibuf, NULL);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void RE_bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
|
|
{
|
|
/* must check before filtering */
|
|
const bool 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RE_bake_ibuf_normalize_displacement(ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max)
|
|
{
|
|
int i;
|
|
const float *current_displacement = displacement;
|
|
const char *current_mask = mask;
|
|
float max_distance;
|
|
|
|
max_distance = max_ff(fabsf(displacement_min), fabsf(displacement_max));
|
|
|
|
for (i = 0; i < ibuf->x * ibuf->y; i++) {
|
|
if (*current_mask == FILTER_MASK_USED) {
|
|
float normalized_displacement;
|
|
|
|
if (max_distance > 1e-5f)
|
|
normalized_displacement = (*current_displacement + max_distance) / (max_distance * 2);
|
|
else
|
|
normalized_displacement = 0.5f;
|
|
|
|
if (ibuf->rect_float) {
|
|
/* currently baking happens to RGBA only */
|
|
float *fp = ibuf->rect_float + i * 4;
|
|
fp[0] = fp[1] = fp[2] = normalized_displacement;
|
|
fp[3] = 1.0f;
|
|
}
|
|
|
|
if (ibuf->rect) {
|
|
unsigned char *cp = (unsigned char *) (ibuf->rect + i);
|
|
cp[0] = cp[1] = cp[2] = FTOCHAR(normalized_displacement);
|
|
cp[3] = 255;
|
|
}
|
|
}
|
|
|
|
current_displacement++;
|
|
current_mask++;
|
|
}
|
|
}
|
|
|
|
/* 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, result = BAKE_RESULT_OK;
|
|
bool use_mask = false;
|
|
bool use_displacement_buffer = false;
|
|
bool do_manage = false;
|
|
|
|
if (ELEM(type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) {
|
|
do_manage = BKE_scene_check_color_management_enabled(re->scene);
|
|
}
|
|
|
|
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;
|
|
|
|
/* do we need buffer to store displacements */
|
|
if (ELEM(type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
|
|
if (((R.r.bake_flag & R_BAKE_NORMALIZE) && R.r.bake_maxdist == 0.0f) ||
|
|
(type == RE_BAKE_DERIVATIVE))
|
|
{
|
|
use_displacement_buffer = true;
|
|
use_mask = true;
|
|
}
|
|
}
|
|
|
|
/* baker uses this flag to detect if image was initialized */
|
|
if ((R.r.bake_flag & R_BAKE_VCOL) == 0) {
|
|
for (ima = G.main->image.first; ima; ima = ima->id.next) {
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
|
ima->id.tag |= LIB_TAG_DOIT;
|
|
ima->flag &= ~IMA_USED_FOR_RENDER;
|
|
if (ibuf) {
|
|
ibuf->userdata = NULL; /* use for masking if needed */
|
|
}
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
}
|
|
}
|
|
|
|
if (R.r.bake_flag & R_BAKE_VCOL) {
|
|
/* untag all meshes */
|
|
BKE_main_id_tag_listbase(&G.main->mesh, LIB_TAG_DOIT, false);
|
|
}
|
|
|
|
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++) {
|
|
handles[a].thread = 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.shi[0].do_manage = do_manage;
|
|
handles[a].ssamp.tot = 1;
|
|
|
|
handles[a].type = type;
|
|
handles[a].actob = actob;
|
|
if (R.r.bake_flag & R_BAKE_VCOL)
|
|
handles[a].zspan = NULL;
|
|
else
|
|
handles[a].zspan = MEM_callocN(sizeof(ZSpan), "zspan for bake");
|
|
|
|
handles[a].use_mask = use_mask;
|
|
handles[a].use_displacement_buffer = use_displacement_buffer;
|
|
|
|
handles[a].do_update = do_update; /* use to tell the view to update */
|
|
|
|
handles[a].displacement_min = FLT_MAX;
|
|
handles[a].displacement_max = -FLT_MAX;
|
|
|
|
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 == false) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* filter and refresh images */
|
|
if ((R.r.bake_flag & R_BAKE_VCOL) == 0) {
|
|
float displacement_min = FLT_MAX, displacement_max = -FLT_MAX;
|
|
|
|
if (use_displacement_buffer) {
|
|
for (a = 0; a < re->r.threads; a++) {
|
|
displacement_min = min_ff(displacement_min, handles[a].displacement_min);
|
|
displacement_max = max_ff(displacement_max, handles[a].displacement_max);
|
|
}
|
|
}
|
|
|
|
for (ima = G.main->image.first; ima; ima = ima->id.next) {
|
|
if ((ima->id.tag & LIB_TAG_DOIT) == 0) {
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
|
|
BakeImBufuserData *userdata;
|
|
|
|
if (ima->flag & IMA_USED_FOR_RENDER)
|
|
result = BAKE_RESULT_FEEDBACK_LOOP;
|
|
|
|
if (!ibuf)
|
|
continue;
|
|
|
|
userdata = (BakeImBufuserData *)ibuf->userdata;
|
|
if (userdata) {
|
|
if (use_displacement_buffer) {
|
|
if (type == RE_BAKE_DERIVATIVE) {
|
|
float user_scale = (R.r.bake_flag & R_BAKE_USERSCALE) ? R.r.bake_user_scale : -1.0f;
|
|
RE_bake_make_derivative(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
|
|
displacement_min, displacement_max, user_scale);
|
|
}
|
|
else {
|
|
RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
|
|
displacement_min, displacement_max);
|
|
}
|
|
}
|
|
|
|
RE_bake_ibuf_filter(ibuf, userdata->mask_buffer, re->r.bake_filter);
|
|
}
|
|
|
|
ibuf->userflags |= IB_BITMAPDIRTY;
|
|
BKE_image_release_ibuf(ima, ibuf, NULL);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* **************** Derivative Maps Baker **************** */
|
|
|
|
static void add_single_heights_margin(const ImBuf *ibuf, const char *mask, float *heights_buffer)
|
|
{
|
|
int x, y;
|
|
|
|
for (y = 0; y < ibuf->y; y++) {
|
|
for (x = 0; x < ibuf->x; x++) {
|
|
int index = ibuf->x * y + x;
|
|
|
|
/* If unassigned pixel, look for neighbors. */
|
|
if (mask[index] != FILTER_MASK_USED) {
|
|
float height_acc = 0;
|
|
int denom = 0;
|
|
int i, j;
|
|
|
|
for (j = -1; j <= 1; j++)
|
|
for (i = -1; i <= 1; i++) {
|
|
int w = (i == 0 ? 1 : 0) + (j == 0 ? 1 : 0) + 1;
|
|
|
|
if (i != 0 || j != 0) {
|
|
int index2 = 0;
|
|
int x0 = x + i;
|
|
int y0 = y + j;
|
|
|
|
CLAMP(x0, 0, ibuf->x - 1);
|
|
CLAMP(y0, 0, ibuf->y - 1);
|
|
|
|
index2 = ibuf->x * y0 + x0;
|
|
|
|
if (mask[index2] == FILTER_MASK_USED) {
|
|
height_acc += w * heights_buffer[index2];
|
|
denom += w;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Insert final value. */
|
|
if (denom > 0) {
|
|
heights_buffer[index] = height_acc / denom;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* returns user-scale */
|
|
float RE_bake_make_derivative(ImBuf *ibuf, float *heights_buffer, const char *mask,
|
|
const float height_min, const float height_max,
|
|
const float fmult)
|
|
{
|
|
const float delta_height = height_max - height_min;
|
|
const float denom = delta_height > 0.0f ? (8 * delta_height) : 1.0f;
|
|
bool auto_range_fit = fmult <= 0.0f;
|
|
float max_num_deriv = -1.0f;
|
|
int x, y, index;
|
|
|
|
/* Need a single margin to calculate good derivatives. */
|
|
add_single_heights_margin(ibuf, mask, heights_buffer);
|
|
|
|
if (auto_range_fit) {
|
|
/* If automatic range fitting is enabled. */
|
|
for (y = 0; y < ibuf->y; y++) {
|
|
const int Yu = y == (ibuf->y - 1) ? (ibuf->y - 1) : (y + 1);
|
|
const int Yc = y;
|
|
const int Yd = y == 0 ? 0 : (y - 1);
|
|
|
|
for (x = 0; x < ibuf->x; x++) {
|
|
const int Xl = x == 0 ? 0 : (x - 1);
|
|
const int Xc = x;
|
|
const int Xr = x == (ibuf->x - 1) ? (ibuf->x - 1) : (x + 1);
|
|
|
|
const float Hcy = heights_buffer[Yc * ibuf->x + Xr] - heights_buffer[Yc * ibuf->x + Xl];
|
|
const float Hu = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yu * ibuf->x + Xl];
|
|
const float Hd = heights_buffer[Yd * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xl];
|
|
|
|
const float Hl = heights_buffer[Yu * ibuf->x + Xl] - heights_buffer[Yd * ibuf->x + Xl];
|
|
const float Hcx = heights_buffer[Yu * ibuf->x + Xc] - heights_buffer[Yd * ibuf->x + Xc];
|
|
const float Hr = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xr];
|
|
|
|
/* This corresponds to using the sobel kernel on the heights buffer
|
|
* to obtain the derivative multiplied by 8.
|
|
*/
|
|
const float deriv_x = Hu + 2 * Hcy + Hd;
|
|
const float deriv_y = Hr + 2 * Hcx + Hl;
|
|
|
|
/* early out */
|
|
index = ibuf->x * y + x;
|
|
if (mask[index] != FILTER_MASK_USED) {
|
|
continue;
|
|
}
|
|
|
|
/* Widen bound. */
|
|
if (fabsf(deriv_x) > max_num_deriv) {
|
|
max_num_deriv = fabsf(deriv_x);
|
|
}
|
|
|
|
if (fabsf(deriv_y) > max_num_deriv) {
|
|
max_num_deriv = fabsf(deriv_y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Output derivatives. */
|
|
auto_range_fit &= (max_num_deriv > 0);
|
|
for (y = 0; y < ibuf->y; y++) {
|
|
const int Yu = y == (ibuf->y - 1) ? (ibuf->y - 1) : (y + 1);
|
|
const int Yc = y;
|
|
const int Yd = y == 0 ? 0 : (y - 1);
|
|
|
|
for (x = 0; x < ibuf->x; x++) {
|
|
const int Xl = x == 0 ? 0 : (x - 1);
|
|
const int Xc = x;
|
|
const int Xr = x == (ibuf->x - 1) ? (ibuf->x - 1) : (x + 1);
|
|
|
|
const float Hcy = heights_buffer[Yc * ibuf->x + Xr] - heights_buffer[Yc * ibuf->x + Xl];
|
|
const float Hu = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yu * ibuf->x + Xl];
|
|
const float Hd = heights_buffer[Yd * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xl];
|
|
|
|
const float Hl = heights_buffer[Yu * ibuf->x + Xl] - heights_buffer[Yd * ibuf->x + Xl];
|
|
const float Hcx = heights_buffer[Yu * ibuf->x + Xc] - heights_buffer[Yd * ibuf->x + Xc];
|
|
const float Hr = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xr];
|
|
|
|
/* This corresponds to using the sobel kernel on the heights buffer
|
|
* to obtain the derivative multiplied by 8.
|
|
*/
|
|
float deriv_x = Hu + 2 * Hcy + Hd;
|
|
float deriv_y = Hr + 2 * Hcx + Hl;
|
|
|
|
/* Early out. */
|
|
index = ibuf->x * y + x;
|
|
if (mask[index] != FILTER_MASK_USED) {
|
|
continue;
|
|
}
|
|
|
|
if (auto_range_fit) {
|
|
deriv_x /= max_num_deriv;
|
|
deriv_y /= max_num_deriv;
|
|
}
|
|
else {
|
|
deriv_x *= (fmult / denom);
|
|
deriv_y *= (fmult / denom);
|
|
}
|
|
|
|
deriv_x = deriv_x * 0.5f + 0.5f;
|
|
deriv_y = deriv_y * 0.5f + 0.5f;
|
|
|
|
/* Clamp. */
|
|
CLAMP(deriv_x, 0.0f, 1.0f);
|
|
CLAMP(deriv_y, 0.0f, 1.0f);
|
|
|
|
/* Write out derivatives. */
|
|
if (ibuf->rect_float) {
|
|
float *rrgbf = ibuf->rect_float + index * 4;
|
|
|
|
rrgbf[0] = deriv_x;
|
|
rrgbf[1] = deriv_y;
|
|
rrgbf[2] = 0.0f;
|
|
rrgbf[3] = 1.0f;
|
|
}
|
|
else {
|
|
char *rrgb = (char *)ibuf->rect + index * 4;
|
|
|
|
rrgb[0] = FTOCHAR(deriv_x);
|
|
rrgb[1] = FTOCHAR(deriv_y);
|
|
rrgb[2] = 0;
|
|
rrgb[3] = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Eeturn user-scale (for rendering). */
|
|
return auto_range_fit ? (max_num_deriv / denom) : (fmult > 0.0f ? (1.0f / fmult) : 0.0f);
|
|
}
|