- Multi-thread BKE_mesh_recalc_looptri. - Add BKE_mesh_recalc_looptri_with_normals, this skips having to calculate normals for ngons. Exact performance depends on number of faces, size of ngons and available CPU cores. For high poly meshes the isolated improvement to BKE_mesh_recalc_looptri in my tests was between 6.7x .. 25.0x, with the largest gains seen in meshes containing ngons with many sides. The overall speedup for high poly meshes containing quads and triangles is only ~20% although ngon heavy meshes can be much faster.
1080 lines
32 KiB
C
1080 lines
32 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup render
|
|
*
|
|
* \brief The API itself is simple.
|
|
* Blender sends a populated array of BakePixels to the renderer,
|
|
* and gets back an array of floats with the result.
|
|
*
|
|
* \section bake_api Development Notes for External Engines
|
|
*
|
|
* The Bake API is fully implemented with Python rna functions.
|
|
* The operator expects/call a function:
|
|
*
|
|
* ``def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)``
|
|
* - scene: current scene (Python object)
|
|
* - object: object to render (Python object)
|
|
* - pass_type: pass to render (string, e.g., "COMBINED", "AO", "NORMAL", ...)
|
|
* - object_id: index of object to bake (to use with the pixel_array)
|
|
* - pixel_array: list of primitive ids and barycentric coordinates to
|
|
* `bake(Python object, see bake_pixel)`.
|
|
* - num_pixels: size of pixel_array, number of pixels to bake (int)
|
|
* - depth: depth of pixels to return (int, assuming always 4 now)
|
|
* - result: array to be populated by the engine (float array, PyLong_AsVoidPtr)
|
|
*
|
|
* \note Normals are expected to be in World Space and in the +X, +Y, +Z orientation.
|
|
*
|
|
* \subsection bake_pixel BakePixel data structure
|
|
*
|
|
* pixel_array is a Python object storing BakePixel elements:
|
|
*
|
|
* \code{.c}
|
|
* struct BakePixel {
|
|
* int primitive_id, object_id;
|
|
* float uv[2];
|
|
* float du_dx, du_dy;
|
|
* float dv_dx, dv_dy;
|
|
* };
|
|
* \endcode
|
|
*
|
|
* In python you have access to:
|
|
* - ``primitive_id``, ``object_id``, ``uv``, ``du_dx``, ``du_dy``, ``next``
|
|
* - ``next()`` is a function that returns the next #BakePixel in the array.
|
|
*
|
|
* \note Pixels that should not be baked have ``primitive_id == -1``
|
|
*
|
|
* For a complete implementation example look at the Cycles Bake commit.
|
|
*/
|
|
|
|
#include <limits.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "BKE_bvhutils.h"
|
|
#include "BKE_customdata.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_mesh_runtime.h"
|
|
#include "BKE_mesh_tangent.h"
|
|
#include "BKE_node.h"
|
|
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "RE_bake.h"
|
|
|
|
/* local include */
|
|
#include "render_types.h"
|
|
#include "zbuf.h"
|
|
|
|
typedef struct BakeDataZSpan {
|
|
BakePixel *pixel_array;
|
|
int primitive_id;
|
|
BakeImage *bk_image;
|
|
ZSpan *zspan;
|
|
float du_dx, du_dy;
|
|
float dv_dx, dv_dy;
|
|
} BakeDataZSpan;
|
|
|
|
/**
|
|
* struct wrapping up tangent space data
|
|
*/
|
|
typedef struct TSpace {
|
|
float tangent[3];
|
|
float sign;
|
|
} TSpace;
|
|
|
|
typedef struct TriTessFace {
|
|
const MVert *mverts[3];
|
|
const TSpace *tspace[3];
|
|
float *loop_normal[3];
|
|
float normal[3]; /* for flat faces */
|
|
bool is_smooth;
|
|
} TriTessFace;
|
|
|
|
static void store_bake_pixel(void *handle, int x, int y, float u, float v)
|
|
{
|
|
BakeDataZSpan *bd = (BakeDataZSpan *)handle;
|
|
BakePixel *pixel;
|
|
|
|
const int width = bd->bk_image->width;
|
|
const size_t offset = bd->bk_image->offset;
|
|
const int i = offset + y * width + x;
|
|
|
|
pixel = &bd->pixel_array[i];
|
|
pixel->primitive_id = bd->primitive_id;
|
|
|
|
/* At this point object_id is always 0, since this function runs for the
|
|
* low-poly mesh only. The object_id lookup indices are set afterwards. */
|
|
|
|
copy_v2_fl2(pixel->uv, u, v);
|
|
|
|
pixel->du_dx = bd->du_dx;
|
|
pixel->du_dy = bd->du_dy;
|
|
pixel->dv_dx = bd->dv_dx;
|
|
pixel->dv_dy = bd->dv_dy;
|
|
pixel->object_id = 0;
|
|
pixel->seed = i;
|
|
}
|
|
|
|
void RE_bake_mask_fill(const BakePixel pixel_array[], const size_t num_pixels, char *mask)
|
|
{
|
|
size_t i;
|
|
if (!mask) {
|
|
return;
|
|
}
|
|
|
|
/* only extend to pixels outside the mask area */
|
|
for (i = 0; i < num_pixels; i++) {
|
|
if (pixel_array[i].primitive_id != -1) {
|
|
mask[i] = FILTER_MASK_USED;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RE_bake_margin(ImBuf *ibuf, char *mask, const int margin)
|
|
{
|
|
/* margin */
|
|
IMB_filter_extend(ibuf, mask, margin);
|
|
|
|
if (ibuf->planes != R_IMF_PLANES_RGBA) {
|
|
/* clear alpha added by filtering */
|
|
IMB_rectfill_alpha(ibuf, 1.0f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function returns the coordinate and normal of a barycentric u,v
|
|
* for a face defined by the primitive_id index.
|
|
* The returned normal is actually the direction from the same barycentric coordinate
|
|
* in the cage to the base mesh
|
|
* The returned coordinate is the point in the cage mesh
|
|
*/
|
|
static void calc_point_from_barycentric_cage(TriTessFace *triangles_low,
|
|
TriTessFace *triangles_cage,
|
|
const float mat_low[4][4],
|
|
const float mat_cage[4][4],
|
|
int primitive_id,
|
|
float u,
|
|
float v,
|
|
float r_co[3],
|
|
float r_dir[3])
|
|
{
|
|
float data[2][3][3];
|
|
float coord[2][3];
|
|
float dir[3];
|
|
int i;
|
|
|
|
TriTessFace *triangle[2];
|
|
|
|
triangle[0] = &triangles_low[primitive_id];
|
|
triangle[1] = &triangles_cage[primitive_id];
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
copy_v3_v3(data[i][0], triangle[i]->mverts[0]->co);
|
|
copy_v3_v3(data[i][1], triangle[i]->mverts[1]->co);
|
|
copy_v3_v3(data[i][2], triangle[i]->mverts[2]->co);
|
|
interp_barycentric_tri_v3(data[i], u, v, coord[i]);
|
|
}
|
|
|
|
/* convert from local to world space */
|
|
mul_m4_v3(mat_low, coord[0]);
|
|
mul_m4_v3(mat_cage, coord[1]);
|
|
|
|
sub_v3_v3v3(dir, coord[0], coord[1]);
|
|
normalize_v3(dir);
|
|
|
|
copy_v3_v3(r_co, coord[1]);
|
|
copy_v3_v3(r_dir, dir);
|
|
}
|
|
|
|
/**
|
|
* This function returns the coordinate and normal of a barycentric u,v
|
|
* for a face defined by the primitive_id index.
|
|
* The returned coordinate is extruded along the normal by cage_extrusion
|
|
*/
|
|
static void calc_point_from_barycentric_extrusion(TriTessFace *triangles,
|
|
const float mat[4][4],
|
|
const float imat[4][4],
|
|
int primitive_id,
|
|
float u,
|
|
float v,
|
|
float cage_extrusion,
|
|
float r_co[3],
|
|
float r_dir[3],
|
|
const bool is_cage)
|
|
{
|
|
float data[3][3];
|
|
float coord[3];
|
|
float dir[3];
|
|
float cage[3];
|
|
bool is_smooth;
|
|
|
|
TriTessFace *triangle = &triangles[primitive_id];
|
|
is_smooth = triangle->is_smooth || is_cage;
|
|
|
|
copy_v3_v3(data[0], triangle->mverts[0]->co);
|
|
copy_v3_v3(data[1], triangle->mverts[1]->co);
|
|
copy_v3_v3(data[2], triangle->mverts[2]->co);
|
|
|
|
interp_barycentric_tri_v3(data, u, v, coord);
|
|
|
|
if (is_smooth) {
|
|
normal_short_to_float_v3(data[0], triangle->mverts[0]->no);
|
|
normal_short_to_float_v3(data[1], triangle->mverts[1]->no);
|
|
normal_short_to_float_v3(data[2], triangle->mverts[2]->no);
|
|
|
|
interp_barycentric_tri_v3(data, u, v, dir);
|
|
normalize_v3(dir);
|
|
}
|
|
else {
|
|
copy_v3_v3(dir, triangle->normal);
|
|
}
|
|
|
|
mul_v3_v3fl(cage, dir, cage_extrusion);
|
|
add_v3_v3(coord, cage);
|
|
|
|
normalize_v3(dir);
|
|
negate_v3(dir);
|
|
|
|
/* convert from local to world space */
|
|
mul_m4_v3(mat, coord);
|
|
mul_transposed_mat3_m4_v3(imat, dir);
|
|
normalize_v3(dir);
|
|
|
|
copy_v3_v3(r_co, coord);
|
|
copy_v3_v3(r_dir, dir);
|
|
}
|
|
|
|
static void barycentric_differentials_from_position(const float co[3],
|
|
const float v1[3],
|
|
const float v2[3],
|
|
const float v3[3],
|
|
const float dxco[3],
|
|
const float dyco[3],
|
|
const float facenor[3],
|
|
const bool differentials,
|
|
float *u,
|
|
float *v,
|
|
float *dx_u,
|
|
float *dx_v,
|
|
float *dy_u,
|
|
float *dy_v)
|
|
{
|
|
/* find most stable axis to project */
|
|
int axis1, axis2;
|
|
axis_dominant_v3(&axis1, &axis2, facenor);
|
|
|
|
/* compute u,v and derivatives */
|
|
float t00 = v3[axis1] - v1[axis1];
|
|
float t01 = v3[axis2] - v1[axis2];
|
|
float t10 = v3[axis1] - v2[axis1];
|
|
float t11 = v3[axis2] - v2[axis2];
|
|
|
|
float detsh = (t00 * t11 - t10 * t01);
|
|
detsh = (detsh != 0.0f) ? 1.0f / detsh : 0.0f;
|
|
t00 *= detsh;
|
|
t01 *= detsh;
|
|
t10 *= detsh;
|
|
t11 *= detsh;
|
|
|
|
*u = (v3[axis1] - co[axis1]) * t11 - (v3[axis2] - co[axis2]) * t10;
|
|
*v = (v3[axis2] - co[axis2]) * t00 - (v3[axis1] - co[axis1]) * t01;
|
|
if (differentials) {
|
|
*dx_u = dxco[axis1] * t11 - dxco[axis2] * t10;
|
|
*dx_v = dxco[axis2] * t00 - dxco[axis1] * t01;
|
|
*dy_u = dyco[axis1] * t11 - dyco[axis2] * t10;
|
|
*dy_v = dyco[axis2] * t00 - dyco[axis1] * t01;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function populates pixel_array and returns TRUE if things are correct
|
|
*/
|
|
static bool cast_ray_highpoly(BVHTreeFromMesh *treeData,
|
|
TriTessFace *triangle_low,
|
|
TriTessFace *triangles[],
|
|
BakePixel *pixel_array_low,
|
|
BakePixel *pixel_array,
|
|
const float mat_low[4][4],
|
|
BakeHighPolyData *highpoly,
|
|
const float co[3],
|
|
const float dir[3],
|
|
const int pixel_id,
|
|
const int tot_highpoly,
|
|
const float max_ray_distance)
|
|
{
|
|
int i;
|
|
int hit_mesh = -1;
|
|
float hit_distance = max_ray_distance;
|
|
if (hit_distance == 0.0f) {
|
|
/* No ray distance set, use maximum. */
|
|
hit_distance = FLT_MAX;
|
|
}
|
|
|
|
BVHTreeRayHit *hits;
|
|
hits = MEM_mallocN(sizeof(BVHTreeRayHit) * tot_highpoly, "Bake Highpoly to Lowpoly: BVH Rays");
|
|
|
|
for (i = 0; i < tot_highpoly; i++) {
|
|
float co_high[3], dir_high[3];
|
|
|
|
hits[i].index = -1;
|
|
/* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */
|
|
hits[i].dist = BVH_RAYCAST_DIST_MAX;
|
|
|
|
/* transform the ray from the world space to the highpoly space */
|
|
mul_v3_m4v3(co_high, highpoly[i].imat, co);
|
|
|
|
/* rotates */
|
|
mul_v3_mat3_m4v3(dir_high, highpoly[i].imat, dir);
|
|
normalize_v3(dir_high);
|
|
|
|
/* cast ray */
|
|
if (treeData[i].tree) {
|
|
BLI_bvhtree_ray_cast(treeData[i].tree,
|
|
co_high,
|
|
dir_high,
|
|
0.0f,
|
|
&hits[i],
|
|
treeData[i].raycast_callback,
|
|
&treeData[i]);
|
|
}
|
|
|
|
if (hits[i].index != -1) {
|
|
float distance;
|
|
float hit_world[3];
|
|
|
|
/* distance comparison in world space */
|
|
mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co);
|
|
distance = len_squared_v3v3(hit_world, co);
|
|
|
|
if (distance < hit_distance) {
|
|
hit_mesh = i;
|
|
hit_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hit_mesh != -1) {
|
|
int primitive_id_high = hits[hit_mesh].index;
|
|
TriTessFace *triangle_high = &triangles[hit_mesh][primitive_id_high];
|
|
BakePixel *pixel_low = &pixel_array_low[pixel_id];
|
|
BakePixel *pixel_high = &pixel_array[pixel_id];
|
|
|
|
pixel_high->primitive_id = primitive_id_high;
|
|
pixel_high->object_id = hit_mesh;
|
|
pixel_high->seed = pixel_id;
|
|
|
|
/* ray direction in high poly object space */
|
|
float dir_high[3];
|
|
mul_v3_mat3_m4v3(dir_high, highpoly[hit_mesh].imat, dir);
|
|
normalize_v3(dir_high);
|
|
|
|
/* compute position differentials on low poly object */
|
|
float duco_low[3], dvco_low[3], dxco[3], dyco[3];
|
|
sub_v3_v3v3(duco_low, triangle_low->mverts[0]->co, triangle_low->mverts[2]->co);
|
|
sub_v3_v3v3(dvco_low, triangle_low->mverts[1]->co, triangle_low->mverts[2]->co);
|
|
|
|
mul_v3_v3fl(dxco, duco_low, pixel_low->du_dx);
|
|
madd_v3_v3fl(dxco, dvco_low, pixel_low->dv_dx);
|
|
mul_v3_v3fl(dyco, duco_low, pixel_low->du_dy);
|
|
madd_v3_v3fl(dyco, dvco_low, pixel_low->dv_dy);
|
|
|
|
/* transform from low poly to high poly object space */
|
|
mul_mat3_m4_v3(mat_low, dxco);
|
|
mul_mat3_m4_v3(mat_low, dyco);
|
|
mul_mat3_m4_v3(highpoly[hit_mesh].imat, dxco);
|
|
mul_mat3_m4_v3(highpoly[hit_mesh].imat, dyco);
|
|
|
|
/* transfer position differentials */
|
|
float tmp[3];
|
|
mul_v3_v3fl(tmp, dir_high, 1.0f / dot_v3v3(dir_high, triangle_high->normal));
|
|
madd_v3_v3fl(dxco, tmp, -dot_v3v3(dxco, triangle_high->normal));
|
|
madd_v3_v3fl(dyco, tmp, -dot_v3v3(dyco, triangle_high->normal));
|
|
|
|
/* compute barycentric differentials from position differentials */
|
|
barycentric_differentials_from_position(hits[hit_mesh].co,
|
|
triangle_high->mverts[0]->co,
|
|
triangle_high->mverts[1]->co,
|
|
triangle_high->mverts[2]->co,
|
|
dxco,
|
|
dyco,
|
|
triangle_high->normal,
|
|
true,
|
|
&pixel_high->uv[0],
|
|
&pixel_high->uv[1],
|
|
&pixel_high->du_dx,
|
|
&pixel_high->dv_dx,
|
|
&pixel_high->du_dy,
|
|
&pixel_high->dv_dy);
|
|
|
|
/* verify we have valid uvs */
|
|
BLI_assert(pixel_high->uv[0] >= -1e-3f && pixel_high->uv[1] >= -1e-3f &&
|
|
pixel_high->uv[0] + pixel_high->uv[1] <= 1.0f + 1e-3f);
|
|
}
|
|
else {
|
|
pixel_array[pixel_id].primitive_id = -1;
|
|
pixel_array[pixel_id].object_id = -1;
|
|
pixel_array[pixel_id].seed = 0;
|
|
}
|
|
|
|
MEM_freeN(hits);
|
|
return hit_mesh != -1;
|
|
}
|
|
|
|
/**
|
|
* This function populates an array of verts for the triangles of a mesh
|
|
* Tangent and Normals are also stored
|
|
*/
|
|
static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval)
|
|
{
|
|
int i;
|
|
MVert *mvert;
|
|
TSpace *tspace = NULL;
|
|
float(*loop_normals)[3] = NULL;
|
|
|
|
const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
|
|
MLoopTri *looptri;
|
|
TriTessFace *triangles;
|
|
|
|
/* calculate normal for each polygon only once */
|
|
unsigned int mpoly_prev = UINT_MAX;
|
|
float no[3];
|
|
|
|
mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
|
|
looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
|
|
triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__);
|
|
|
|
const float(*precomputed_normals)[3] = CustomData_get_layer(&me->pdata, CD_NORMAL);
|
|
const bool calculate_normal = precomputed_normals ? false : true;
|
|
|
|
if (precomputed_normals != NULL) {
|
|
BKE_mesh_recalc_looptri_with_normals(
|
|
me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri, precomputed_normals);
|
|
}
|
|
else {
|
|
BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
|
|
}
|
|
|
|
if (tangent) {
|
|
BKE_mesh_ensure_normals_for_display(me_eval);
|
|
BKE_mesh_calc_normals_split(me_eval);
|
|
BKE_mesh_calc_loop_tangents(me_eval, true, NULL, 0);
|
|
|
|
tspace = CustomData_get_layer(&me_eval->ldata, CD_TANGENT);
|
|
BLI_assert(tspace);
|
|
|
|
loop_normals = CustomData_get_layer(&me_eval->ldata, CD_NORMAL);
|
|
}
|
|
|
|
for (i = 0; i < tottri; i++) {
|
|
const MLoopTri *lt = &looptri[i];
|
|
const MPoly *mp = &me->mpoly[lt->poly];
|
|
|
|
triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v];
|
|
triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v];
|
|
triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v];
|
|
triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0;
|
|
|
|
if (tangent) {
|
|
triangles[i].tspace[0] = &tspace[lt->tri[0]];
|
|
triangles[i].tspace[1] = &tspace[lt->tri[1]];
|
|
triangles[i].tspace[2] = &tspace[lt->tri[2]];
|
|
}
|
|
|
|
if (loop_normals) {
|
|
triangles[i].loop_normal[0] = loop_normals[lt->tri[0]];
|
|
triangles[i].loop_normal[1] = loop_normals[lt->tri[1]];
|
|
triangles[i].loop_normal[2] = loop_normals[lt->tri[2]];
|
|
}
|
|
|
|
if (calculate_normal) {
|
|
if (lt->poly != mpoly_prev) {
|
|
BKE_mesh_calc_poly_normal(mp, &me->mloop[mp->loopstart], me->mvert, no);
|
|
mpoly_prev = lt->poly;
|
|
}
|
|
copy_v3_v3(triangles[i].normal, no);
|
|
}
|
|
else {
|
|
copy_v3_v3(triangles[i].normal, precomputed_normals[lt->poly]);
|
|
}
|
|
}
|
|
|
|
MEM_freeN(looptri);
|
|
|
|
return triangles;
|
|
}
|
|
|
|
bool RE_bake_pixels_populate_from_objects(struct Mesh *me_low,
|
|
BakePixel pixel_array_from[],
|
|
BakePixel pixel_array_to[],
|
|
BakeHighPolyData highpoly[],
|
|
const int tot_highpoly,
|
|
const size_t num_pixels,
|
|
const bool is_custom_cage,
|
|
const float cage_extrusion,
|
|
const float max_ray_distance,
|
|
float mat_low[4][4],
|
|
float mat_cage[4][4],
|
|
struct Mesh *me_cage)
|
|
{
|
|
size_t i;
|
|
int primitive_id;
|
|
float u, v;
|
|
float imat_low[4][4];
|
|
bool is_cage = me_cage != NULL;
|
|
bool result = true;
|
|
|
|
Mesh *me_eval_low = NULL;
|
|
Mesh **me_highpoly;
|
|
BVHTreeFromMesh *treeData;
|
|
|
|
/* Note: all coordinates are in local space */
|
|
TriTessFace *tris_low = NULL;
|
|
TriTessFace *tris_cage = NULL;
|
|
TriTessFace **tris_high;
|
|
|
|
/* assume all lowpoly tessfaces can be quads */
|
|
tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array");
|
|
|
|
/* assume all highpoly tessfaces are triangles */
|
|
me_highpoly = MEM_mallocN(sizeof(Mesh *) * tot_highpoly, "Highpoly Derived Meshes");
|
|
treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees");
|
|
|
|
if (!is_cage) {
|
|
me_eval_low = BKE_mesh_copy_for_eval(me_low, false);
|
|
tris_low = mesh_calc_tri_tessface(me_low, true, me_eval_low);
|
|
}
|
|
else if (is_custom_cage) {
|
|
tris_low = mesh_calc_tri_tessface(me_low, false, NULL);
|
|
tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL);
|
|
}
|
|
else {
|
|
tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL);
|
|
}
|
|
|
|
invert_m4_m4(imat_low, mat_low);
|
|
|
|
for (i = 0; i < tot_highpoly; i++) {
|
|
tris_high[i] = mesh_calc_tri_tessface(highpoly[i].me, false, NULL);
|
|
|
|
me_highpoly[i] = highpoly[i].me;
|
|
BKE_mesh_runtime_looptri_ensure(me_highpoly[i]);
|
|
|
|
if (me_highpoly[i]->runtime.looptris.len != 0) {
|
|
/* Create a bvh-tree for each highpoly object */
|
|
BKE_bvhtree_from_mesh_get(&treeData[i], me_highpoly[i], BVHTREE_FROM_LOOPTRI, 2);
|
|
|
|
if (treeData[i].tree == NULL) {
|
|
printf("Baking: out of memory while creating BHVTree for object \"%s\"\n",
|
|
highpoly[i].ob->id.name + 2);
|
|
result = false;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_pixels; i++) {
|
|
float co[3];
|
|
float dir[3];
|
|
TriTessFace *tri_low;
|
|
|
|
primitive_id = pixel_array_from[i].primitive_id;
|
|
|
|
if (primitive_id == -1) {
|
|
pixel_array_to[i].primitive_id = -1;
|
|
continue;
|
|
}
|
|
|
|
u = pixel_array_from[i].uv[0];
|
|
v = pixel_array_from[i].uv[1];
|
|
|
|
/* calculate from low poly mesh cage */
|
|
if (is_custom_cage) {
|
|
calc_point_from_barycentric_cage(
|
|
tris_low, tris_cage, mat_low, mat_cage, primitive_id, u, v, co, dir);
|
|
tri_low = &tris_cage[primitive_id];
|
|
}
|
|
else if (is_cage) {
|
|
calc_point_from_barycentric_extrusion(
|
|
tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true);
|
|
tri_low = &tris_cage[primitive_id];
|
|
}
|
|
else {
|
|
calc_point_from_barycentric_extrusion(
|
|
tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false);
|
|
tri_low = &tris_low[primitive_id];
|
|
}
|
|
|
|
/* cast ray */
|
|
if (!cast_ray_highpoly(treeData,
|
|
tri_low,
|
|
tris_high,
|
|
pixel_array_from,
|
|
pixel_array_to,
|
|
mat_low,
|
|
highpoly,
|
|
co,
|
|
dir,
|
|
i,
|
|
tot_highpoly,
|
|
max_ray_distance)) {
|
|
/* if it fails mask out the original pixel array */
|
|
pixel_array_from[i].primitive_id = -1;
|
|
}
|
|
}
|
|
|
|
/* garbage collection */
|
|
cleanup:
|
|
for (i = 0; i < tot_highpoly; i++) {
|
|
free_bvhtree_from_mesh(&treeData[i]);
|
|
|
|
if (tris_high[i]) {
|
|
MEM_freeN(tris_high[i]);
|
|
}
|
|
}
|
|
|
|
MEM_freeN(tris_high);
|
|
MEM_freeN(treeData);
|
|
MEM_freeN(me_highpoly);
|
|
|
|
if (me_eval_low) {
|
|
BKE_id_free(NULL, me_eval_low);
|
|
}
|
|
if (tris_low) {
|
|
MEM_freeN(tris_low);
|
|
}
|
|
if (tris_cage) {
|
|
MEM_freeN(tris_cage);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void bake_differentials(BakeDataZSpan *bd,
|
|
const float *uv1,
|
|
const float *uv2,
|
|
const float *uv3)
|
|
{
|
|
float A;
|
|
|
|
/* assumes dPdu = P1 - P3 and dPdv = P2 - P3 */
|
|
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;
|
|
|
|
bd->du_dx = (uv2[1] - uv3[1]) * A;
|
|
bd->dv_dx = (uv3[1] - uv1[1]) * A;
|
|
|
|
bd->du_dy = (uv3[0] - uv2[0]) * A;
|
|
bd->dv_dy = (uv1[0] - uv3[0]) * A;
|
|
}
|
|
else {
|
|
bd->du_dx = bd->du_dy = 0.0f;
|
|
bd->dv_dx = bd->dv_dy = 0.0f;
|
|
}
|
|
}
|
|
|
|
void RE_bake_pixels_populate(Mesh *me,
|
|
BakePixel pixel_array[],
|
|
const size_t num_pixels,
|
|
const BakeTargets *targets,
|
|
const char *uv_layer)
|
|
{
|
|
const MLoopUV *mloopuv;
|
|
if ((uv_layer == NULL) || (uv_layer[0] == '\0')) {
|
|
mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
|
|
}
|
|
else {
|
|
int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer);
|
|
mloopuv = CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, uv_id);
|
|
}
|
|
|
|
if (mloopuv == NULL) {
|
|
return;
|
|
}
|
|
|
|
BakeDataZSpan bd;
|
|
bd.pixel_array = pixel_array;
|
|
bd.zspan = MEM_callocN(sizeof(ZSpan) * targets->num_images, "bake zspan");
|
|
|
|
/* initialize all pixel arrays so we know which ones are 'blank' */
|
|
for (int i = 0; i < num_pixels; i++) {
|
|
pixel_array[i].primitive_id = -1;
|
|
pixel_array[i].object_id = 0;
|
|
}
|
|
|
|
for (int i = 0; i < targets->num_images; i++) {
|
|
zbuf_alloc_span(&bd.zspan[i], targets->images[i].width, targets->images[i].height);
|
|
}
|
|
|
|
const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
|
|
MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
|
|
|
|
BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
|
|
|
|
for (int i = 0; i < tottri; i++) {
|
|
const MLoopTri *lt = &looptri[i];
|
|
const MPoly *mp = &me->mpoly[lt->poly];
|
|
float vec[3][2];
|
|
int mat_nr = mp->mat_nr;
|
|
int image_id = targets->material_to_image[mat_nr];
|
|
|
|
if (image_id < 0) {
|
|
continue;
|
|
}
|
|
|
|
bd.bk_image = &targets->images[image_id];
|
|
bd.primitive_id = i;
|
|
|
|
for (int a = 0; a < 3; a++) {
|
|
const float *uv = mloopuv[lt->tri[a]].uv;
|
|
|
|
/* 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 T18685 - Campbell */
|
|
vec[a][0] = uv[0] * (float)bd.bk_image->width - (0.5f + 0.001f);
|
|
vec[a][1] = uv[1] * (float)bd.bk_image->height - (0.5f + 0.002f);
|
|
}
|
|
|
|
bake_differentials(&bd, vec[0], vec[1], vec[2]);
|
|
zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel);
|
|
}
|
|
|
|
for (int i = 0; i < targets->num_images; i++) {
|
|
zbuf_free_span(&bd.zspan[i]);
|
|
}
|
|
|
|
MEM_freeN(looptri);
|
|
MEM_freeN(bd.zspan);
|
|
}
|
|
|
|
/* ******************** NORMALS ************************ */
|
|
|
|
/**
|
|
* convert a normalized normal to the -1.0 1.0 range
|
|
* the input is expected to be POS_X, POS_Y, POS_Z
|
|
*/
|
|
static void normal_uncompress(float out[3], const float in[3])
|
|
{
|
|
int i;
|
|
for (i = 0; i < 3; i++) {
|
|
out[i] = 2.0f * in[i] - 1.0f;
|
|
}
|
|
}
|
|
|
|
static void normal_compress(float out[3],
|
|
const float in[3],
|
|
const eBakeNormalSwizzle normal_swizzle[3])
|
|
{
|
|
const int swizzle_index[6] = {
|
|
0, /* R_BAKE_POSX */
|
|
1, /* R_BAKE_POSY */
|
|
2, /* R_BAKE_POSZ */
|
|
0, /* R_BAKE_NEGX */
|
|
1, /* R_BAKE_NEGY */
|
|
2, /* R_BAKE_NEGZ */
|
|
};
|
|
const float swizzle_sign[6] = {
|
|
+1.0f, /* R_BAKE_POSX */
|
|
+1.0f, /* R_BAKE_POSY */
|
|
+1.0f, /* R_BAKE_POSZ */
|
|
-1.0f, /* R_BAKE_NEGX */
|
|
-1.0f, /* R_BAKE_NEGY */
|
|
-1.0f, /* R_BAKE_NEGZ */
|
|
};
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
int index;
|
|
float sign;
|
|
|
|
sign = swizzle_sign[normal_swizzle[i]];
|
|
index = swizzle_index[normal_swizzle[i]];
|
|
|
|
/*
|
|
* There is a small 1e-5f bias for precision issues. otherwise
|
|
* we randomly get 127 or 128 for neutral colors in tangent maps.
|
|
* we choose 128 because it is the convention flat color. *
|
|
*/
|
|
|
|
out[i] = sign * in[index] / 2.0f + 0.5f + 1e-5f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function converts an object space normal map
|
|
* to a tangent space normal map for a given low poly mesh.
|
|
*/
|
|
void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[],
|
|
const size_t num_pixels,
|
|
const int depth,
|
|
float result[],
|
|
Mesh *me,
|
|
const eBakeNormalSwizzle normal_swizzle[3],
|
|
float mat[4][4])
|
|
{
|
|
size_t i;
|
|
|
|
TriTessFace *triangles;
|
|
|
|
Mesh *me_eval = BKE_mesh_copy_for_eval(me, false);
|
|
|
|
triangles = mesh_calc_tri_tessface(me, true, me_eval);
|
|
|
|
BLI_assert(num_pixels >= 3);
|
|
|
|
for (i = 0; i < num_pixels; i++) {
|
|
TriTessFace *triangle;
|
|
float tangents[3][3];
|
|
float normals[3][3];
|
|
float signs[3];
|
|
int j;
|
|
|
|
float tangent[3];
|
|
float normal[3];
|
|
float binormal[3];
|
|
float sign;
|
|
float u, v, w;
|
|
|
|
float tsm[3][3]; /* tangent space matrix */
|
|
float itsm[3][3];
|
|
|
|
size_t offset;
|
|
float nor[3]; /* texture normal */
|
|
|
|
bool is_smooth;
|
|
|
|
int primitive_id = pixel_array[i].primitive_id;
|
|
|
|
offset = i * depth;
|
|
|
|
if (primitive_id == -1) {
|
|
if (depth == 4) {
|
|
copy_v4_fl4(&result[offset], 0.5f, 0.5f, 1.0f, 1.0f);
|
|
}
|
|
else {
|
|
copy_v3_fl3(&result[offset], 0.5f, 0.5f, 1.0f);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
triangle = &triangles[primitive_id];
|
|
is_smooth = triangle->is_smooth;
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
const TSpace *ts;
|
|
|
|
if (is_smooth) {
|
|
if (triangle->loop_normal[j]) {
|
|
copy_v3_v3(normals[j], triangle->loop_normal[j]);
|
|
}
|
|
else {
|
|
normal_short_to_float_v3(normals[j], triangle->mverts[j]->no);
|
|
}
|
|
}
|
|
|
|
ts = triangle->tspace[j];
|
|
copy_v3_v3(tangents[j], ts->tangent);
|
|
signs[j] = ts->sign;
|
|
}
|
|
|
|
u = pixel_array[i].uv[0];
|
|
v = pixel_array[i].uv[1];
|
|
w = 1.0f - u - v;
|
|
|
|
/* normal */
|
|
if (is_smooth) {
|
|
interp_barycentric_tri_v3(normals, u, v, normal);
|
|
}
|
|
else {
|
|
copy_v3_v3(normal, triangle->normal);
|
|
}
|
|
|
|
/* tangent */
|
|
interp_barycentric_tri_v3(tangents, u, v, tangent);
|
|
|
|
/* sign */
|
|
/* The sign is the same at all face vertices for any non degenerate face.
|
|
* Just in case we clamp the interpolated value though. */
|
|
sign = (signs[0] * u + signs[1] * v + signs[2] * w) < 0 ? (-1.0f) : 1.0f;
|
|
|
|
/* binormal */
|
|
/* B = sign * cross(N, T) */
|
|
cross_v3_v3v3(binormal, normal, tangent);
|
|
mul_v3_fl(binormal, sign);
|
|
|
|
/* populate tangent space matrix */
|
|
copy_v3_v3(tsm[0], tangent);
|
|
copy_v3_v3(tsm[1], binormal);
|
|
copy_v3_v3(tsm[2], normal);
|
|
|
|
/* texture values */
|
|
normal_uncompress(nor, &result[offset]);
|
|
|
|
/* converts from world space to local space */
|
|
mul_transposed_mat3_m4_v3(mat, nor);
|
|
|
|
invert_m3_m3(itsm, tsm);
|
|
mul_m3_v3(itsm, nor);
|
|
normalize_v3(nor);
|
|
|
|
/* save back the values */
|
|
normal_compress(&result[offset], nor, normal_swizzle);
|
|
}
|
|
|
|
/* garbage collection */
|
|
MEM_freeN(triangles);
|
|
|
|
if (me_eval) {
|
|
BKE_id_free(NULL, me_eval);
|
|
}
|
|
}
|
|
|
|
void RE_bake_normal_world_to_object(const BakePixel pixel_array[],
|
|
const size_t num_pixels,
|
|
const int depth,
|
|
float result[],
|
|
struct Object *ob,
|
|
const eBakeNormalSwizzle normal_swizzle[3])
|
|
{
|
|
size_t i;
|
|
float iobmat[4][4];
|
|
|
|
invert_m4_m4(iobmat, ob->obmat);
|
|
|
|
for (i = 0; i < num_pixels; i++) {
|
|
size_t offset;
|
|
float nor[3];
|
|
|
|
if (pixel_array[i].primitive_id == -1) {
|
|
continue;
|
|
}
|
|
|
|
offset = i * depth;
|
|
normal_uncompress(nor, &result[offset]);
|
|
|
|
/* rotates only without translation */
|
|
mul_mat3_m4_v3(iobmat, nor);
|
|
normalize_v3(nor);
|
|
|
|
/* save back the values */
|
|
normal_compress(&result[offset], nor, normal_swizzle);
|
|
}
|
|
}
|
|
|
|
void RE_bake_normal_world_to_world(const BakePixel pixel_array[],
|
|
const size_t num_pixels,
|
|
const int depth,
|
|
float result[],
|
|
const eBakeNormalSwizzle normal_swizzle[3])
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < num_pixels; i++) {
|
|
size_t offset;
|
|
float nor[3];
|
|
|
|
if (pixel_array[i].primitive_id == -1) {
|
|
continue;
|
|
}
|
|
|
|
offset = i * depth;
|
|
normal_uncompress(nor, &result[offset]);
|
|
|
|
/* save back the values */
|
|
normal_compress(&result[offset], nor, normal_swizzle);
|
|
}
|
|
}
|
|
|
|
void RE_bake_ibuf_clear(Image *image, const bool is_tangent)
|
|
{
|
|
ImBuf *ibuf;
|
|
void *lock;
|
|
|
|
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};
|
|
|
|
ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
|
|
BLI_assert(ibuf);
|
|
|
|
if (is_tangent) {
|
|
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
|
|
}
|
|
else {
|
|
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
|
|
}
|
|
|
|
BKE_image_release_ibuf(image, ibuf, lock);
|
|
}
|
|
|
|
/* ************************************************************* */
|
|
|
|
int RE_pass_depth(const eScenePassType pass_type)
|
|
{
|
|
/* IMB_buffer_byte_from_float assumes 4 channels
|
|
* making it work for now - XXX */
|
|
return 4;
|
|
|
|
switch (pass_type) {
|
|
case SCE_PASS_Z:
|
|
case SCE_PASS_AO:
|
|
case SCE_PASS_MIST: {
|
|
return 1;
|
|
}
|
|
case SCE_PASS_UV: {
|
|
return 2;
|
|
}
|
|
case SCE_PASS_COMBINED:
|
|
case SCE_PASS_SHADOW:
|
|
case SCE_PASS_NORMAL:
|
|
case SCE_PASS_VECTOR:
|
|
case SCE_PASS_INDEXOB: /* XXX double check */
|
|
case SCE_PASS_RAYHITS: /* XXX double check */
|
|
case SCE_PASS_EMIT:
|
|
case SCE_PASS_ENVIRONMENT:
|
|
case SCE_PASS_INDEXMA:
|
|
case SCE_PASS_DIFFUSE_DIRECT:
|
|
case SCE_PASS_DIFFUSE_INDIRECT:
|
|
case SCE_PASS_DIFFUSE_COLOR:
|
|
case SCE_PASS_GLOSSY_DIRECT:
|
|
case SCE_PASS_GLOSSY_INDIRECT:
|
|
case SCE_PASS_GLOSSY_COLOR:
|
|
case SCE_PASS_TRANSM_DIRECT:
|
|
case SCE_PASS_TRANSM_INDIRECT:
|
|
case SCE_PASS_TRANSM_COLOR:
|
|
case SCE_PASS_SUBSURFACE_DIRECT:
|
|
case SCE_PASS_SUBSURFACE_INDIRECT:
|
|
case SCE_PASS_SUBSURFACE_COLOR:
|
|
default: {
|
|
return 3;
|
|
}
|
|
}
|
|
}
|