From 880b0f981dce558d0661c1b838753fb7a07945c1 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 9 Nov 2020 15:42:38 +0100 Subject: Cleanup: more renaming in the render/ module for consistency --- source/blender/render/CMakeLists.txt | 7 +- source/blender/render/RE_render_ext.h | 80 -- source/blender/render/RE_shader_ext.h | 83 -- source/blender/render/RE_texture.h | 121 +++ source/blender/render/intern/bake.c | 1070 ++++++++++++++++++++ source/blender/render/intern/bake_api.c | 1070 -------------------- source/blender/render/intern/engine.c | 989 ++++++++++++++++++ source/blender/render/intern/external_engine.c | 989 ------------------ source/blender/render/intern/multires_bake.c | 7 +- source/blender/render/intern/pipeline.c | 2 +- source/blender/render/intern/texture_image.c | 3 +- .../blender/render/intern/texture_pointdensity.c | 3 +- source/blender/render/intern/texture_procedural.c | 3 +- 13 files changed, 2193 insertions(+), 2234 deletions(-) delete mode 100644 source/blender/render/RE_render_ext.h delete mode 100644 source/blender/render/RE_shader_ext.h create mode 100644 source/blender/render/RE_texture.h create mode 100644 source/blender/render/intern/bake.c delete mode 100644 source/blender/render/intern/bake_api.c create mode 100644 source/blender/render/intern/engine.c delete mode 100644 source/blender/render/intern/external_engine.c (limited to 'source/blender/render') diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index 6e229fd25b7..2516c015924 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -42,8 +42,8 @@ set(INC set(SRC - intern/bake_api.c - intern/external_engine.c + intern/bake.c + intern/engine.c intern/initrender.c intern/multires_bake.c intern/pipeline.c @@ -57,8 +57,7 @@ set(SRC RE_engine.h RE_multires_bake.h RE_pipeline.h - RE_render_ext.h - RE_shader_ext.h + RE_texture.h intern/initrender.h intern/render_result.h diff --git a/source/blender/render/RE_render_ext.h b/source/blender/render/RE_render_ext.h deleted file mode 100644 index 28a22399892..00000000000 --- a/source/blender/render/RE_render_ext.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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) 2006 by Blender Foundation - * All rights reserved. - */ -/** \file - * \ingroup render - */ - -#pragma once - -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -/* this include is for non-render pipeline exports (still old cruft here) */ -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -/* called by meshtools */ -struct Depsgraph; -struct ImagePool; -struct MTex; - -#ifdef __cplusplus -extern "C" { -#endif - -/* texture_procedural.c */ -bool RE_texture_evaluate(const struct MTex *mtex, - const float vec[3], - const int thread, - struct ImagePool *pool, - const bool skip_load_image, - const bool texnode_preview, - /* Return arguments. */ - float *r_intensity, - float r_rgba[4]) ATTR_NONNULL(1, 2, 7, 8); - -void texture_rgb_blend( - float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype); -float texture_value_blend(float tex, float out, float fact, float facg, int blendtype); - -void RE_texture_rng_init(void); -void RE_texture_rng_exit(void); - -/* texture_image.c */ -void ibuf_sample(struct ImBuf *ibuf, float fx, float fy, float dx, float dy, float result[4]); - -/* texture_pointdensity.c */ -struct PointDensity; - -void RE_point_density_cache(struct Depsgraph *depsgraph, struct PointDensity *pd); - -void RE_point_density_minmax(struct Depsgraph *depsgraph, - struct PointDensity *pd, - float r_min[3], - float r_max[3]); - -void RE_point_density_sample(struct Depsgraph *depsgraph, - struct PointDensity *pd, - const int resolution, - float *values); - -void RE_point_density_free(struct PointDensity *pd); - -void RE_point_density_fix_linking(void); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/render/RE_shader_ext.h b/source/blender/render/RE_shader_ext.h deleted file mode 100644 index d9d5a924949..00000000000 --- a/source/blender/render/RE_shader_ext.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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) 2006 by Blender Foundation - * All rights reserved. - */ -/** \file - * \ingroup render - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -/* this include is for texture exports */ -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -/* localized texture result data */ -/* note; tr tg tb ta has to remain in this order */ -typedef struct TexResult { - float tin, tr, tg, tb, ta; - int talpha; - float *nor; -} TexResult; - -typedef struct BakeImBufuserData { - float *displacement_buffer; - char *mask_buffer; -} BakeImBufuserData; - -/* node shaders... */ -struct ImagePool; -struct MTex; -struct Tex; - -/* this one uses nodes */ -int multitex_ext(struct Tex *tex, - float texvec[3], - float dxt[3], - float dyt[3], - int osatex, - struct TexResult *texres, - const short thread, - struct ImagePool *pool, - bool scene_color_manage, - const bool skip_load_image); -/* nodes disabled */ -int multitex_ext_safe(struct Tex *tex, - const float texvec[3], - struct TexResult *texres, - struct ImagePool *pool, - bool scene_color_manage, - const bool skip_load_image); -/* only for internal node usage */ -int multitex_nodes(struct Tex *tex, - const float texvec[3], - float dxt[3], - float dyt[3], - int osatex, - struct TexResult *texres, - const short thread, - short which_output, - struct MTex *mtex, - struct ImagePool *pool); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/render/RE_texture.h b/source/blender/render/RE_texture.h new file mode 100644 index 00000000000..b394cfeee75 --- /dev/null +++ b/source/blender/render/RE_texture.h @@ -0,0 +1,121 @@ +/* + * 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) 2006 by Blender Foundation + * All rights reserved. + */ +/** \file + * \ingroup render + */ + +#pragma once + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* this include is for non-render pipeline exports (still old cruft here) */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* called by meshtools */ +struct Depsgraph; +struct ImagePool; +struct MTex; +struct Tex; + +#ifdef __cplusplus +extern "C" { +#endif + +/* texture_procedural.c */ +bool RE_texture_evaluate(const struct MTex *mtex, + const float vec[3], + const int thread, + struct ImagePool *pool, + const bool skip_load_image, + const bool texnode_preview, + /* Return arguments. */ + float *r_intensity, + float r_rgba[4]) ATTR_NONNULL(1, 2, 7, 8); + +void texture_rgb_blend( + float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype); +float texture_value_blend(float tex, float out, float fact, float facg, int blendtype); + +void RE_texture_rng_init(void); +void RE_texture_rng_exit(void); + +/* texture_image.c */ +void ibuf_sample(struct ImBuf *ibuf, float fx, float fy, float dx, float dy, float result[4]); + +/* texture_pointdensity.c */ +struct PointDensity; + +void RE_point_density_cache(struct Depsgraph *depsgraph, struct PointDensity *pd); + +void RE_point_density_minmax(struct Depsgraph *depsgraph, + struct PointDensity *pd, + float r_min[3], + float r_max[3]); + +void RE_point_density_sample(struct Depsgraph *depsgraph, + struct PointDensity *pd, + const int resolution, + float *values); + +void RE_point_density_free(struct PointDensity *pd); + +void RE_point_density_fix_linking(void); + +/* texture_procedural.c */ + +/* Texture evaluation result. + * Note; tr tg tb ta has to remain in this order for array access. */ +typedef struct TexResult { + float tin, tr, tg, tb, ta; + int talpha; + float *nor; +} TexResult; + +/* This one uses nodes. */ +int multitex_ext(struct Tex *tex, + float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + struct TexResult *texres, + const short thread, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image); +/* Nodes disabled. */ +int multitex_ext_safe(struct Tex *tex, + const float texvec[3], + struct TexResult *texres, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image); +/* Only for internal node usage. */ +int multitex_nodes(struct Tex *tex, + const float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + struct TexResult *texres, + const short thread, + short which_output, + struct MTex *mtex, + struct ImagePool *pool); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c new file mode 100644 index 00000000000..6f5db4986f2 --- /dev/null +++ b/source/blender/render/intern/bake.c @@ -0,0 +1,1070 @@ +/* + * 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 + +#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; +} + +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; + + /* 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; + } + + 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__); + + 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); + } + + const float *precomputed_normals = CustomData_get_layer(&me->pdata, CD_NORMAL); + const bool calculate_normal = precomputed_normals ? false : true; + + 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 BakeImages *bake_images, + 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) * bake_images->size, "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 < bake_images->size; i++) { + zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[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 = bake_images->lookup[mat_nr]; + + if (image_id < 0) { + continue; + } + + bd.bk_image = &bake_images->data[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 < bake_images->size; 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; + } + } +} diff --git a/source/blender/render/intern/bake_api.c b/source/blender/render/intern/bake_api.c deleted file mode 100644 index 6f5db4986f2..00000000000 --- a/source/blender/render/intern/bake_api.c +++ /dev/null @@ -1,1070 +0,0 @@ -/* - * 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 - -#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; -} - -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; - - /* 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; - } - - 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__); - - 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); - } - - const float *precomputed_normals = CustomData_get_layer(&me->pdata, CD_NORMAL); - const bool calculate_normal = precomputed_normals ? false : true; - - 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 BakeImages *bake_images, - 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) * bake_images->size, "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 < bake_images->size; i++) { - zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[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 = bake_images->lookup[mat_nr]; - - if (image_id < 0) { - continue; - } - - bd.bk_image = &bake_images->data[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 < bake_images->size; 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; - } - } -} diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c new file mode 100644 index 00000000000..9a3dc0d000c --- /dev/null +++ b/source/blender/render/intern/engine.c @@ -0,0 +1,989 @@ +/* + * 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) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup render + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLT_translation.h" + +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math_bits.h" +#include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_colortools.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_node.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_debug.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +#include "RE_bake.h" +#include "RE_engine.h" +#include "RE_pipeline.h" + +#include "DRW_engine.h" + +#include "initrender.h" +#include "pipeline.h" +#include "render_result.h" +#include "render_types.h" + +/* Render Engine Types */ + +ListBase R_engines = {NULL, NULL}; + +void RE_engines_init(void) +{ + DRW_engines_register(); +} + +void RE_engines_exit(void) +{ + RenderEngineType *type, *next; + + DRW_engines_free(); + + for (type = R_engines.first; type; type = next) { + next = type->next; + + BLI_remlink(&R_engines, type); + + if (!(type->flag & RE_INTERNAL)) { + if (type->rna_ext.free) { + type->rna_ext.free(type->rna_ext.data); + } + + MEM_freeN(type); + } + } +} + +void RE_engines_register(RenderEngineType *render_type) +{ + if (render_type->draw_engine) { + DRW_engine_register(render_type->draw_engine); + } + BLI_addtail(&R_engines, render_type); +} + +RenderEngineType *RE_engines_find(const char *idname) +{ + RenderEngineType *type; + + type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname)); + if (!type) { + type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname)); + } + + return type; +} + +bool RE_engine_is_external(const Render *re) +{ + return (re->engine && re->engine->type && re->engine->type->render); +} + +bool RE_engine_is_opengl(RenderEngineType *render_type) +{ + /* TODO refine? Can we have ogl render engine without ogl render pipeline? */ + return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine); +} + +/* Create, Free */ + +RenderEngine *RE_engine_create(RenderEngineType *type) +{ + RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine"); + engine->type = type; + + BLI_mutex_init(&engine->update_render_passes_mutex); + + return engine; +} + +void RE_engine_free(RenderEngine *engine) +{ +#ifdef WITH_PYTHON + if (engine->py_instance) { + BPY_DECREF_RNA_INVALIDATE(engine->py_instance); + } +#endif + + BLI_mutex_end(&engine->update_render_passes_mutex); + + MEM_freeN(engine); +} + +/* Bake Render Results */ + +static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y, int w, int h) +{ + /* Create render result with specified size. */ + RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); + + rr->rectx = w; + rr->recty = h; + rr->tilerect.xmin = x; + rr->tilerect.ymin = y; + rr->tilerect.xmax = x + w; + rr->tilerect.ymax = y + h; + + /* Add single baking render layer. */ + RenderLayer *rl = MEM_callocN(sizeof(RenderLayer), "bake render layer"); + rl->rectx = w; + rl->recty = h; + BLI_addtail(&rr->layers, rl); + + /* Add render passes. */ + RenderPass *result_pass = render_layer_add_pass( + rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA"); + RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA"); + RenderPass *differential_pass = render_layer_add_pass(rr, rl, 4, "BakeDifferential", "", "RGBA"); + + /* Fill render passes from bake pixel array, to be read by the render engine. */ + for (int ty = 0; ty < h; ty++) { + size_t offset = ty * w * 4; + float *primitive = primitive_pass->rect + offset; + float *differential = differential_pass->rect + offset; + + size_t bake_offset = (y + ty) * engine->bake.width + x; + const BakePixel *bake_pixel = engine->bake.pixels + bake_offset; + + for (int tx = 0; tx < w; tx++) { + if (bake_pixel->object_id != engine->bake.object_id) { + primitive[0] = int_as_float(-1); + primitive[1] = int_as_float(-1); + } + else { + primitive[0] = int_as_float(bake_pixel->object_id); + primitive[1] = int_as_float(bake_pixel->primitive_id); + primitive[2] = bake_pixel->uv[0]; + primitive[3] = bake_pixel->uv[1]; + + differential[0] = bake_pixel->du_dx; + differential[1] = bake_pixel->du_dy; + differential[2] = bake_pixel->dv_dx; + differential[3] = bake_pixel->dv_dy; + } + + primitive += 4; + differential += 4; + bake_pixel++; + } + } + + /* Initialize tile render result from full image bake result. */ + for (int ty = 0; ty < h; ty++) { + size_t offset = ty * w * engine->bake.depth; + size_t bake_offset = ((y + ty) * engine->bake.width + x) * engine->bake.depth; + size_t size = w * engine->bake.depth * sizeof(float); + + memcpy(result_pass->rect + offset, engine->bake.result + bake_offset, size); + } + + return rr; +} + +static void render_result_to_bake(RenderEngine *engine, RenderResult *rr) +{ + RenderPass *rpass = RE_pass_find_by_name(rr->layers.first, RE_PASSNAME_COMBINED, ""); + + if (!rpass) { + return; + } + + /* Copy from tile render result to full image bake result. */ + int x = rr->tilerect.xmin; + int y = rr->tilerect.ymin; + int w = rr->tilerect.xmax - rr->tilerect.xmin; + int h = rr->tilerect.ymax - rr->tilerect.ymin; + + for (int ty = 0; ty < h; ty++) { + size_t offset = ty * w * engine->bake.depth; + size_t bake_offset = ((y + ty) * engine->bake.width + x) * engine->bake.depth; + size_t size = w * engine->bake.depth * sizeof(float); + + memcpy(engine->bake.result + bake_offset, rpass->rect + offset, size); + } +} + +/* Render Results */ + +static RenderPart *get_part_from_result(Render *re, RenderResult *result) +{ + rcti key = result->tilerect; + BLI_rcti_translate(&key, re->disprect.xmin, re->disprect.ymin); + + return BLI_ghash_lookup(re->parts, &key); +} + +RenderResult *RE_engine_begin_result( + RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) +{ + if (engine->bake.pixels) { + RenderResult *result = render_result_from_bake(engine, x, y, w, h); + BLI_addtail(&engine->fullresult, result); + return result; + } + + Render *re = engine->re; + RenderResult *result; + rcti disprect; + + /* ensure the coordinates are within the right limits */ + CLAMP(x, 0, re->result->rectx); + CLAMP(y, 0, re->result->recty); + CLAMP(w, 0, re->result->rectx); + CLAMP(h, 0, re->result->recty); + + if (x + w > re->result->rectx) { + w = re->result->rectx - x; + } + if (y + h > re->result->recty) { + h = re->result->recty - y; + } + + /* allocate a render result */ + disprect.xmin = x; + disprect.xmax = x + w; + disprect.ymin = y; + disprect.ymax = y + h; + + result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); + + /* todo: make this thread safe */ + + /* can be NULL if we CLAMP the width or height to 0 */ + if (result) { + render_result_clone_passes(re, result, viewname); + + RenderPart *pa; + + /* Copy EXR tile settings, so pipeline knows whether this is a result + * for Save Buffers enabled rendering. + */ + result->do_exr_tile = re->result->do_exr_tile; + + BLI_addtail(&engine->fullresult, result); + + result->tilerect.xmin += re->disprect.xmin; + result->tilerect.xmax += re->disprect.xmin; + result->tilerect.ymin += re->disprect.ymin; + result->tilerect.ymax += re->disprect.ymin; + + pa = get_part_from_result(re, result); + + if (pa) { + pa->status = PART_STATUS_IN_PROGRESS; + } + } + + return result; +} + +void RE_engine_update_result(RenderEngine *engine, RenderResult *result) +{ + if (engine->bake.pixels) { + /* No interactive baking updates for now. */ + return; + } + + Render *re = engine->re; + + if (result) { + render_result_merge(re->result, result); + result->renlay = result->layers.first; /* weak, draws first layer always */ + re->display_update(re->duh, result, NULL); + } +} + +void RE_engine_add_pass(RenderEngine *engine, + const char *name, + int channels, + const char *chan_id, + const char *layername) +{ + Render *re = engine->re; + + if (!re || !re->result) { + return; + } + + RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL); +} + +void RE_engine_end_result( + RenderEngine *engine, RenderResult *result, bool cancel, bool highlight, bool merge_results) +{ + Render *re = engine->re; + + if (!result) { + return; + } + + if (engine->bake.pixels) { + render_result_to_bake(engine, result); + BLI_remlink(&engine->fullresult, result); + render_result_free(result); + return; + } + + /* merge. on break, don't merge in result for preview renders, looks nicer */ + if (!highlight) { + /* for exr tile render, detect tiles that are done */ + RenderPart *pa = get_part_from_result(re, result); + + if (pa) { + pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; + } + else if (re->result->do_exr_tile) { + /* if written result does not match any tile and we are using save + * buffers, we are going to get openexr save errors */ + fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); + } + } + + if (!cancel || merge_results) { + if (re->result->do_exr_tile) { + if (!cancel && merge_results) { + render_result_exr_file_merge(re->result, result, re->viewname); + render_result_merge(re->result, result); + } + } + else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { + render_result_merge(re->result, result); + } + + /* draw */ + if (!re->test_break(re->tbh)) { + result->renlay = result->layers.first; /* weak, draws first layer always */ + re->display_update(re->duh, result, NULL); + } + } + + /* free */ + BLI_remlink(&engine->fullresult, result); + render_result_free(result); +} + +RenderResult *RE_engine_get_result(RenderEngine *engine) +{ + return engine->re->result; +} + +/* Cancel */ + +bool RE_engine_test_break(RenderEngine *engine) +{ + Render *re = engine->re; + + if (re) { + return re->test_break(re->tbh); + } + + return 0; +} + +/* Statistics */ + +void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) +{ + Render *re = engine->re; + + /* stats draw callback */ + if (re) { + re->i.statstr = stats; + re->i.infostr = info; + re->stats_draw(re->sdh, &re->i); + re->i.infostr = NULL; + re->i.statstr = NULL; + } + + /* set engine text */ + engine->text[0] = '\0'; + + if (stats && stats[0] && info && info[0]) { + BLI_snprintf(engine->text, sizeof(engine->text), "%s | %s", stats, info); + } + else if (info && info[0]) { + BLI_strncpy(engine->text, info, sizeof(engine->text)); + } + else if (stats && stats[0]) { + BLI_strncpy(engine->text, stats, sizeof(engine->text)); + } +} + +void RE_engine_update_progress(RenderEngine *engine, float progress) +{ + Render *re = engine->re; + + if (re) { + CLAMP(progress, 0.0f, 1.0f); + re->progress(re->prh, progress); + } +} + +void RE_engine_update_memory_stats(RenderEngine *engine, float mem_used, float mem_peak) +{ + Render *re = engine->re; + + if (re) { + re->i.mem_used = mem_used; + re->i.mem_peak = mem_peak; + } +} + +void RE_engine_report(RenderEngine *engine, int type, const char *msg) +{ + Render *re = engine->re; + + if (re) { + BKE_report(engine->re->reports, type, msg); + } + else if (engine->reports) { + BKE_report(engine->reports, type, msg); + } +} + +void RE_engine_set_error_message(RenderEngine *engine, const char *msg) +{ + Render *re = engine->re; + if (re != NULL) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + if (rr->error != NULL) { + MEM_freeN(rr->error); + } + rr->error = BLI_strdup(msg); + } + RE_ReleaseResult(re); + } +} + +const char *RE_engine_active_view_get(RenderEngine *engine) +{ + Render *re = engine->re; + return RE_GetActiveRenderView(re); +} + +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) +{ + Render *re = engine->re; + RE_SetActiveRenderView(re, viewname); +} + +float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool use_spherical_stereo) +{ + /* When using spherical stereo, get camera shift without multiview, + * leaving stereo to be handled by the engine. */ + Render *re = engine->re; + if (use_spherical_stereo || re == NULL) { + return BKE_camera_multiview_shift_x(NULL, camera, NULL); + } + + return BKE_camera_multiview_shift_x(&re->r, camera, re->viewname); +} + +void RE_engine_get_camera_model_matrix(RenderEngine *engine, + Object *camera, + bool use_spherical_stereo, + float *r_modelmat) +{ + /* When using spherical stereo, get model matrix without multiview, + * leaving stereo to be handled by the engine. */ + Render *re = engine->re; + if (use_spherical_stereo || re == NULL) { + BKE_camera_multiview_model_matrix(NULL, camera, NULL, (float(*)[4])r_modelmat); + } + else { + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, (float(*)[4])r_modelmat); + } +} + +bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera) +{ + Render *re = engine->re; + return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0; +} + +rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) +{ + static rcti tiles_static[BLENDER_MAX_THREADS]; + const int allocation_step = BLENDER_MAX_THREADS; + int total_tiles = 0; + rcti *tiles = tiles_static; + int allocation_size = BLENDER_MAX_THREADS; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); + + *r_needs_free = false; + + if (!re->parts || (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0)) { + *r_total_tiles = 0; + BLI_rw_mutex_unlock(&re->partsmutex); + return NULL; + } + + GHashIterator pa_iter; + GHASH_ITER (pa_iter, re->parts) { + RenderPart *pa = BLI_ghashIterator_getValue(&pa_iter); + if (pa->status == PART_STATUS_IN_PROGRESS) { + if (total_tiles >= allocation_size) { + /* Just in case we're using crazy network rendering with more + * workers than BLENDER_MAX_THREADS. + */ + allocation_size += allocation_step; + if (tiles == tiles_static) { + /* Can not realloc yet, tiles are pointing to a + * stack memory. + */ + tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles"); + } + else { + tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti)); + } + *r_needs_free = true; + } + tiles[total_tiles] = pa->disprect; + + total_tiles++; + } + } + BLI_rw_mutex_unlock(&re->partsmutex); + *r_total_tiles = total_tiles; + return tiles; +} + +RenderData *RE_engine_get_render_data(Render *re) +{ + return &re->r; +} + +/* Depsgraph */ +static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer) +{ + Main *bmain = engine->re->main; + Scene *scene = engine->re->scene; + + engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); + DEG_debug_name_set(engine->depsgraph, "RENDER"); + + if (engine->re->r.scemode & R_BUTS_PREVIEW) { + Depsgraph *depsgraph = engine->depsgraph; + DEG_graph_relations_update(depsgraph); + DEG_evaluate_on_framechange(depsgraph, CFRA); + DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true); + DEG_ids_clear_recalc(bmain, depsgraph); + } + else { + BKE_scene_graph_update_for_newframe(engine->depsgraph); + } +} + +static void engine_depsgraph_free(RenderEngine *engine) +{ + DEG_graph_free(engine->depsgraph); + + engine->depsgraph = NULL; +} + +void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe) +{ + if (!engine->depsgraph) { + return; + } + + Render *re = engine->re; + double cfra = (double)frame + (double)subframe; + + CLAMP(cfra, MINAFRAME, MAXFRAME); + BKE_scene_frame_set(re->scene, cfra); + BKE_scene_graph_update_for_newframe(engine->depsgraph); + + BKE_scene_camera_switch_update(re->scene); +} + +/* Bake */ +void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene) +{ + re->scene = scene; + re->main = bmain; + render_copy_renderdata(&re->r, &scene->r); +} + +bool RE_bake_has_engine(Render *re) +{ + RenderEngineType *type = RE_engines_find(re->r.engine); + return (type->bake != NULL); +} + +bool RE_bake_engine(Render *re, + Depsgraph *depsgraph, + Object *object, + const int object_id, + const BakePixel pixel_array[], + const BakeImages *bake_images, + const int depth, + const eScenePassType pass_type, + const int pass_filter, + float result[]) +{ + RenderEngineType *type = RE_engines_find(re->r.engine); + RenderEngine *engine; + bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; + + /* set render info */ + re->i.cfra = re->scene->r.cfra; + BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2); + + /* render */ + engine = re->engine; + + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } + + engine->flag |= RE_ENGINE_RENDERING; + + /* TODO: actually link to a parent which shouldn't happen */ + engine->re = re; + + engine->resolution_x = re->winx; + engine->resolution_y = re->winy; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + RE_parts_init(re); + engine->tile_x = re->r.tilex; + engine->tile_y = re->r.tiley; + BLI_rw_mutex_unlock(&re->partsmutex); + + if (type->bake) { + engine->depsgraph = depsgraph; + + /* update is only called so we create the engine.session */ + if (type->update) { + type->update(engine, re->main, engine->depsgraph); + } + + for (int i = 0; i < bake_images->size; i++) { + const BakeImage *image = bake_images->data + i; + + engine->bake.pixels = pixel_array + image->offset; + engine->bake.result = result + image->offset * depth; + engine->bake.width = image->width; + engine->bake.height = image->height; + engine->bake.depth = depth; + engine->bake.object_id = object_id; + + type->bake( + engine, engine->depsgraph, object, pass_type, pass_filter, image->width, image->height); + + memset(&engine->bake, 0, sizeof(engine->bake)); + } + + engine->depsgraph = NULL; + } + + engine->tile_x = 0; + engine->tile_y = 0; + engine->flag &= ~RE_ENGINE_RENDERING; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + + /* re->engine becomes zero if user changed active render engine during render */ + if (!persistent_data || !re->engine) { + RE_engine_free(engine); + re->engine = NULL; + } + + RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); + + if (BKE_reports_contain(re->reports, RPT_ERROR)) { + G.is_break = true; + } + + return true; +} + +/* Render */ + +int RE_engine_render(Render *re, int do_all) +{ + RenderEngineType *type = RE_engines_find(re->r.engine); + RenderEngine *engine; + bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; + + /* verify if we can render */ + if (!type->render) { + return 0; + } + if ((re->r.scemode & R_BUTS_PREVIEW) && !(type->flag & RE_USE_PREVIEW)) { + return 0; + } + if (do_all && !(type->flag & RE_USE_POSTPROCESS)) { + return 0; + } + if (!do_all && (type->flag & RE_USE_POSTPROCESS)) { + return 0; + } + + /* Lock drawing in UI during data phase. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 1); + } + + /* update animation here so any render layer animation is applied before + * creating the render result */ + if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { + render_update_anim_renderdata(re, &re->scene->r, &re->scene->view_layers); + } + + /* create render result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { + int savebuffers = RR_USE_MEM; + + if (re->result) { + render_result_free(re->result); + } + + if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { + savebuffers = RR_USE_EXR; + } + re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + } + BLI_rw_mutex_unlock(&re->resultmutex); + + if (re->result == NULL) { + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + /* Too small image is handled earlier, here it could only happen if + * there was no sufficient memory to allocate all passes. + */ + BKE_report(re->reports, RPT_ERROR, "Failed allocate render result, out of memory"); + G.is_break = true; + return 1; + } + + /* set render info */ + re->i.cfra = re->scene->r.cfra; + BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name)); + + /* render */ + engine = re->engine; + + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } + + engine->flag |= RE_ENGINE_RENDERING; + + /* TODO: actually link to a parent which shouldn't happen */ + engine->re = re; + + if (re->flag & R_ANIMATION) { + engine->flag |= RE_ENGINE_ANIMATION; + } + if (re->r.scemode & R_BUTS_PREVIEW) { + engine->flag |= RE_ENGINE_PREVIEW; + } + engine->camera_override = re->camera_override; + + engine->resolution_x = re->winx; + engine->resolution_y = re->winy; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + RE_parts_init(re); + engine->tile_x = re->partx; + engine->tile_y = re->party; + BLI_rw_mutex_unlock(&re->partsmutex); + + if (re->result->do_exr_tile) { + render_result_exr_file_begin(re, engine); + } + + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + + if (type->render) { + FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { + if (re->draw_lock) { + re->draw_lock(re->dlh, 1); + } + + ViewLayer *view_layer = BLI_findstring( + &re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)); + engine_depsgraph_init(engine, view_layer); + + if (type->update) { + type->update(engine, re->main, engine->depsgraph); + } + + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + + if (engine->type->flag & RE_USE_GPU_CONTEXT) { + DRW_render_context_enable(engine->re); + } + + type->render(engine, engine->depsgraph); + + if (engine->type->flag & RE_USE_GPU_CONTEXT) { + DRW_render_context_disable(engine->re); + } + + /* Grease pencil render over previous render result. + * + * NOTE: External engine might have been requested to free its + * dependency graph, which is only allowed if there is no grease + * pencil (pipeline is taking care of that). + */ + if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) { + DRW_render_gpencil(engine, engine->depsgraph); + } + + engine_depsgraph_free(engine); + + if (RE_engine_test_break(engine)) { + break; + } + } + FOREACH_VIEW_LAYER_TO_RENDER_END; + } + + engine->tile_x = 0; + engine->tile_y = 0; + engine->flag &= ~RE_ENGINE_RENDERING; + + render_result_free_list(&engine->fullresult, engine->fullresult.first); + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + + if (re->result->do_exr_tile) { + render_result_exr_file_end(re, engine); + } + + /* re->engine becomes zero if user changed active render engine during render */ + if (!persistent_data || !re->engine) { + RE_engine_free(engine); + re->engine = NULL; + } + + if (re->r.scemode & R_EXR_CACHE_FILE) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_cache_write(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); + + if (BKE_reports_contain(re->reports, RPT_ERROR)) { + G.is_break = true; + } + +#ifdef WITH_FREESTYLE + if (re->r.mode & R_EDGE_FRS) { + RE_RenderFreestyleExternal(re); + } +#endif + + return 1; +} + +void RE_engine_update_render_passes(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer, + update_render_passes_cb_t callback, + void *callback_data) +{ + if (!(scene && view_layer && engine && callback && engine->type->update_render_passes)) { + return; + } + + BLI_mutex_lock(&engine->update_render_passes_mutex); + + engine->update_render_passes_cb = callback; + engine->update_render_passes_data = callback_data; + engine->type->update_render_passes(engine, scene, view_layer); + engine->update_render_passes_cb = NULL; + engine->update_render_passes_data = NULL; + + BLI_mutex_unlock(&engine->update_render_passes_mutex); +} + +void RE_engine_register_pass(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer, + const char *name, + int channels, + const char *chanid, + eNodeSocketDatatype type) +{ + if (!(scene && view_layer && engine && engine->update_render_passes_cb)) { + return; + } + + engine->update_render_passes_cb( + engine->update_render_passes_data, scene, view_layer, name, channels, chanid, type); +} + +void RE_engine_free_blender_memory(RenderEngine *engine) +{ + /* Weak way to save memory, but not crash grease pencil. + * + * TODO(sergey): Find better solution for this. + */ + if (DRW_render_check_grease_pencil(engine->depsgraph)) { + return; + } + DEG_graph_free(engine->depsgraph); + engine->depsgraph = NULL; +} diff --git a/source/blender/render/intern/external_engine.c b/source/blender/render/intern/external_engine.c deleted file mode 100644 index 9a3dc0d000c..00000000000 --- a/source/blender/render/intern/external_engine.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * 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) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup render - */ - -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "BLT_translation.h" - -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math_bits.h" -#include "BLI_rect.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "DNA_object_types.h" - -#include "BKE_camera.h" -#include "BKE_colortools.h" -#include "BKE_global.h" -#include "BKE_layer.h" -#include "BKE_node.h" -#include "BKE_report.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_debug.h" -#include "DEG_depsgraph_query.h" - -#include "RNA_access.h" - -#ifdef WITH_PYTHON -# include "BPY_extern.h" -#endif - -#include "RE_bake.h" -#include "RE_engine.h" -#include "RE_pipeline.h" - -#include "DRW_engine.h" - -#include "initrender.h" -#include "pipeline.h" -#include "render_result.h" -#include "render_types.h" - -/* Render Engine Types */ - -ListBase R_engines = {NULL, NULL}; - -void RE_engines_init(void) -{ - DRW_engines_register(); -} - -void RE_engines_exit(void) -{ - RenderEngineType *type, *next; - - DRW_engines_free(); - - for (type = R_engines.first; type; type = next) { - next = type->next; - - BLI_remlink(&R_engines, type); - - if (!(type->flag & RE_INTERNAL)) { - if (type->rna_ext.free) { - type->rna_ext.free(type->rna_ext.data); - } - - MEM_freeN(type); - } - } -} - -void RE_engines_register(RenderEngineType *render_type) -{ - if (render_type->draw_engine) { - DRW_engine_register(render_type->draw_engine); - } - BLI_addtail(&R_engines, render_type); -} - -RenderEngineType *RE_engines_find(const char *idname) -{ - RenderEngineType *type; - - type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname)); - if (!type) { - type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname)); - } - - return type; -} - -bool RE_engine_is_external(const Render *re) -{ - return (re->engine && re->engine->type && re->engine->type->render); -} - -bool RE_engine_is_opengl(RenderEngineType *render_type) -{ - /* TODO refine? Can we have ogl render engine without ogl render pipeline? */ - return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine); -} - -/* Create, Free */ - -RenderEngine *RE_engine_create(RenderEngineType *type) -{ - RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine"); - engine->type = type; - - BLI_mutex_init(&engine->update_render_passes_mutex); - - return engine; -} - -void RE_engine_free(RenderEngine *engine) -{ -#ifdef WITH_PYTHON - if (engine->py_instance) { - BPY_DECREF_RNA_INVALIDATE(engine->py_instance); - } -#endif - - BLI_mutex_end(&engine->update_render_passes_mutex); - - MEM_freeN(engine); -} - -/* Bake Render Results */ - -static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y, int w, int h) -{ - /* Create render result with specified size. */ - RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); - - rr->rectx = w; - rr->recty = h; - rr->tilerect.xmin = x; - rr->tilerect.ymin = y; - rr->tilerect.xmax = x + w; - rr->tilerect.ymax = y + h; - - /* Add single baking render layer. */ - RenderLayer *rl = MEM_callocN(sizeof(RenderLayer), "bake render layer"); - rl->rectx = w; - rl->recty = h; - BLI_addtail(&rr->layers, rl); - - /* Add render passes. */ - RenderPass *result_pass = render_layer_add_pass( - rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA"); - RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA"); - RenderPass *differential_pass = render_layer_add_pass(rr, rl, 4, "BakeDifferential", "", "RGBA"); - - /* Fill render passes from bake pixel array, to be read by the render engine. */ - for (int ty = 0; ty < h; ty++) { - size_t offset = ty * w * 4; - float *primitive = primitive_pass->rect + offset; - float *differential = differential_pass->rect + offset; - - size_t bake_offset = (y + ty) * engine->bake.width + x; - const BakePixel *bake_pixel = engine->bake.pixels + bake_offset; - - for (int tx = 0; tx < w; tx++) { - if (bake_pixel->object_id != engine->bake.object_id) { - primitive[0] = int_as_float(-1); - primitive[1] = int_as_float(-1); - } - else { - primitive[0] = int_as_float(bake_pixel->object_id); - primitive[1] = int_as_float(bake_pixel->primitive_id); - primitive[2] = bake_pixel->uv[0]; - primitive[3] = bake_pixel->uv[1]; - - differential[0] = bake_pixel->du_dx; - differential[1] = bake_pixel->du_dy; - differential[2] = bake_pixel->dv_dx; - differential[3] = bake_pixel->dv_dy; - } - - primitive += 4; - differential += 4; - bake_pixel++; - } - } - - /* Initialize tile render result from full image bake result. */ - for (int ty = 0; ty < h; ty++) { - size_t offset = ty * w * engine->bake.depth; - size_t bake_offset = ((y + ty) * engine->bake.width + x) * engine->bake.depth; - size_t size = w * engine->bake.depth * sizeof(float); - - memcpy(result_pass->rect + offset, engine->bake.result + bake_offset, size); - } - - return rr; -} - -static void render_result_to_bake(RenderEngine *engine, RenderResult *rr) -{ - RenderPass *rpass = RE_pass_find_by_name(rr->layers.first, RE_PASSNAME_COMBINED, ""); - - if (!rpass) { - return; - } - - /* Copy from tile render result to full image bake result. */ - int x = rr->tilerect.xmin; - int y = rr->tilerect.ymin; - int w = rr->tilerect.xmax - rr->tilerect.xmin; - int h = rr->tilerect.ymax - rr->tilerect.ymin; - - for (int ty = 0; ty < h; ty++) { - size_t offset = ty * w * engine->bake.depth; - size_t bake_offset = ((y + ty) * engine->bake.width + x) * engine->bake.depth; - size_t size = w * engine->bake.depth * sizeof(float); - - memcpy(engine->bake.result + bake_offset, rpass->rect + offset, size); - } -} - -/* Render Results */ - -static RenderPart *get_part_from_result(Render *re, RenderResult *result) -{ - rcti key = result->tilerect; - BLI_rcti_translate(&key, re->disprect.xmin, re->disprect.ymin); - - return BLI_ghash_lookup(re->parts, &key); -} - -RenderResult *RE_engine_begin_result( - RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) -{ - if (engine->bake.pixels) { - RenderResult *result = render_result_from_bake(engine, x, y, w, h); - BLI_addtail(&engine->fullresult, result); - return result; - } - - Render *re = engine->re; - RenderResult *result; - rcti disprect; - - /* ensure the coordinates are within the right limits */ - CLAMP(x, 0, re->result->rectx); - CLAMP(y, 0, re->result->recty); - CLAMP(w, 0, re->result->rectx); - CLAMP(h, 0, re->result->recty); - - if (x + w > re->result->rectx) { - w = re->result->rectx - x; - } - if (y + h > re->result->recty) { - h = re->result->recty - y; - } - - /* allocate a render result */ - disprect.xmin = x; - disprect.xmax = x + w; - disprect.ymin = y; - disprect.ymax = y + h; - - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); - - /* todo: make this thread safe */ - - /* can be NULL if we CLAMP the width or height to 0 */ - if (result) { - render_result_clone_passes(re, result, viewname); - - RenderPart *pa; - - /* Copy EXR tile settings, so pipeline knows whether this is a result - * for Save Buffers enabled rendering. - */ - result->do_exr_tile = re->result->do_exr_tile; - - BLI_addtail(&engine->fullresult, result); - - result->tilerect.xmin += re->disprect.xmin; - result->tilerect.xmax += re->disprect.xmin; - result->tilerect.ymin += re->disprect.ymin; - result->tilerect.ymax += re->disprect.ymin; - - pa = get_part_from_result(re, result); - - if (pa) { - pa->status = PART_STATUS_IN_PROGRESS; - } - } - - return result; -} - -void RE_engine_update_result(RenderEngine *engine, RenderResult *result) -{ - if (engine->bake.pixels) { - /* No interactive baking updates for now. */ - return; - } - - Render *re = engine->re; - - if (result) { - render_result_merge(re->result, result); - result->renlay = result->layers.first; /* weak, draws first layer always */ - re->display_update(re->duh, result, NULL); - } -} - -void RE_engine_add_pass(RenderEngine *engine, - const char *name, - int channels, - const char *chan_id, - const char *layername) -{ - Render *re = engine->re; - - if (!re || !re->result) { - return; - } - - RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL); -} - -void RE_engine_end_result( - RenderEngine *engine, RenderResult *result, bool cancel, bool highlight, bool merge_results) -{ - Render *re = engine->re; - - if (!result) { - return; - } - - if (engine->bake.pixels) { - render_result_to_bake(engine, result); - BLI_remlink(&engine->fullresult, result); - render_result_free(result); - return; - } - - /* merge. on break, don't merge in result for preview renders, looks nicer */ - if (!highlight) { - /* for exr tile render, detect tiles that are done */ - RenderPart *pa = get_part_from_result(re, result); - - if (pa) { - pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; - } - else if (re->result->do_exr_tile) { - /* if written result does not match any tile and we are using save - * buffers, we are going to get openexr save errors */ - fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); - } - } - - if (!cancel || merge_results) { - if (re->result->do_exr_tile) { - if (!cancel && merge_results) { - render_result_exr_file_merge(re->result, result, re->viewname); - render_result_merge(re->result, result); - } - } - else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { - render_result_merge(re->result, result); - } - - /* draw */ - if (!re->test_break(re->tbh)) { - result->renlay = result->layers.first; /* weak, draws first layer always */ - re->display_update(re->duh, result, NULL); - } - } - - /* free */ - BLI_remlink(&engine->fullresult, result); - render_result_free(result); -} - -RenderResult *RE_engine_get_result(RenderEngine *engine) -{ - return engine->re->result; -} - -/* Cancel */ - -bool RE_engine_test_break(RenderEngine *engine) -{ - Render *re = engine->re; - - if (re) { - return re->test_break(re->tbh); - } - - return 0; -} - -/* Statistics */ - -void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) -{ - Render *re = engine->re; - - /* stats draw callback */ - if (re) { - re->i.statstr = stats; - re->i.infostr = info; - re->stats_draw(re->sdh, &re->i); - re->i.infostr = NULL; - re->i.statstr = NULL; - } - - /* set engine text */ - engine->text[0] = '\0'; - - if (stats && stats[0] && info && info[0]) { - BLI_snprintf(engine->text, sizeof(engine->text), "%s | %s", stats, info); - } - else if (info && info[0]) { - BLI_strncpy(engine->text, info, sizeof(engine->text)); - } - else if (stats && stats[0]) { - BLI_strncpy(engine->text, stats, sizeof(engine->text)); - } -} - -void RE_engine_update_progress(RenderEngine *engine, float progress) -{ - Render *re = engine->re; - - if (re) { - CLAMP(progress, 0.0f, 1.0f); - re->progress(re->prh, progress); - } -} - -void RE_engine_update_memory_stats(RenderEngine *engine, float mem_used, float mem_peak) -{ - Render *re = engine->re; - - if (re) { - re->i.mem_used = mem_used; - re->i.mem_peak = mem_peak; - } -} - -void RE_engine_report(RenderEngine *engine, int type, const char *msg) -{ - Render *re = engine->re; - - if (re) { - BKE_report(engine->re->reports, type, msg); - } - else if (engine->reports) { - BKE_report(engine->reports, type, msg); - } -} - -void RE_engine_set_error_message(RenderEngine *engine, const char *msg) -{ - Render *re = engine->re; - if (re != NULL) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - if (rr->error != NULL) { - MEM_freeN(rr->error); - } - rr->error = BLI_strdup(msg); - } - RE_ReleaseResult(re); - } -} - -const char *RE_engine_active_view_get(RenderEngine *engine) -{ - Render *re = engine->re; - return RE_GetActiveRenderView(re); -} - -void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) -{ - Render *re = engine->re; - RE_SetActiveRenderView(re, viewname); -} - -float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool use_spherical_stereo) -{ - /* When using spherical stereo, get camera shift without multiview, - * leaving stereo to be handled by the engine. */ - Render *re = engine->re; - if (use_spherical_stereo || re == NULL) { - return BKE_camera_multiview_shift_x(NULL, camera, NULL); - } - - return BKE_camera_multiview_shift_x(&re->r, camera, re->viewname); -} - -void RE_engine_get_camera_model_matrix(RenderEngine *engine, - Object *camera, - bool use_spherical_stereo, - float *r_modelmat) -{ - /* When using spherical stereo, get model matrix without multiview, - * leaving stereo to be handled by the engine. */ - Render *re = engine->re; - if (use_spherical_stereo || re == NULL) { - BKE_camera_multiview_model_matrix(NULL, camera, NULL, (float(*)[4])r_modelmat); - } - else { - BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, (float(*)[4])r_modelmat); - } -} - -bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera) -{ - Render *re = engine->re; - return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0; -} - -rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) -{ - static rcti tiles_static[BLENDER_MAX_THREADS]; - const int allocation_step = BLENDER_MAX_THREADS; - int total_tiles = 0; - rcti *tiles = tiles_static; - int allocation_size = BLENDER_MAX_THREADS; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); - - *r_needs_free = false; - - if (!re->parts || (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0)) { - *r_total_tiles = 0; - BLI_rw_mutex_unlock(&re->partsmutex); - return NULL; - } - - GHashIterator pa_iter; - GHASH_ITER (pa_iter, re->parts) { - RenderPart *pa = BLI_ghashIterator_getValue(&pa_iter); - if (pa->status == PART_STATUS_IN_PROGRESS) { - if (total_tiles >= allocation_size) { - /* Just in case we're using crazy network rendering with more - * workers than BLENDER_MAX_THREADS. - */ - allocation_size += allocation_step; - if (tiles == tiles_static) { - /* Can not realloc yet, tiles are pointing to a - * stack memory. - */ - tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles"); - } - else { - tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti)); - } - *r_needs_free = true; - } - tiles[total_tiles] = pa->disprect; - - total_tiles++; - } - } - BLI_rw_mutex_unlock(&re->partsmutex); - *r_total_tiles = total_tiles; - return tiles; -} - -RenderData *RE_engine_get_render_data(Render *re) -{ - return &re->r; -} - -/* Depsgraph */ -static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer) -{ - Main *bmain = engine->re->main; - Scene *scene = engine->re->scene; - - engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); - DEG_debug_name_set(engine->depsgraph, "RENDER"); - - if (engine->re->r.scemode & R_BUTS_PREVIEW) { - Depsgraph *depsgraph = engine->depsgraph; - DEG_graph_relations_update(depsgraph); - DEG_evaluate_on_framechange(depsgraph, CFRA); - DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true); - DEG_ids_clear_recalc(bmain, depsgraph); - } - else { - BKE_scene_graph_update_for_newframe(engine->depsgraph); - } -} - -static void engine_depsgraph_free(RenderEngine *engine) -{ - DEG_graph_free(engine->depsgraph); - - engine->depsgraph = NULL; -} - -void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe) -{ - if (!engine->depsgraph) { - return; - } - - Render *re = engine->re; - double cfra = (double)frame + (double)subframe; - - CLAMP(cfra, MINAFRAME, MAXFRAME); - BKE_scene_frame_set(re->scene, cfra); - BKE_scene_graph_update_for_newframe(engine->depsgraph); - - BKE_scene_camera_switch_update(re->scene); -} - -/* Bake */ -void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene) -{ - re->scene = scene; - re->main = bmain; - render_copy_renderdata(&re->r, &scene->r); -} - -bool RE_bake_has_engine(Render *re) -{ - RenderEngineType *type = RE_engines_find(re->r.engine); - return (type->bake != NULL); -} - -bool RE_bake_engine(Render *re, - Depsgraph *depsgraph, - Object *object, - const int object_id, - const BakePixel pixel_array[], - const BakeImages *bake_images, - const int depth, - const eScenePassType pass_type, - const int pass_filter, - float result[]) -{ - RenderEngineType *type = RE_engines_find(re->r.engine); - RenderEngine *engine; - bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; - - /* set render info */ - re->i.cfra = re->scene->r.cfra; - BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2); - - /* render */ - engine = re->engine; - - if (!engine) { - engine = RE_engine_create(type); - re->engine = engine; - } - - engine->flag |= RE_ENGINE_RENDERING; - - /* TODO: actually link to a parent which shouldn't happen */ - engine->re = re; - - engine->resolution_x = re->winx; - engine->resolution_y = re->winy; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - RE_parts_init(re); - engine->tile_x = re->r.tilex; - engine->tile_y = re->r.tiley; - BLI_rw_mutex_unlock(&re->partsmutex); - - if (type->bake) { - engine->depsgraph = depsgraph; - - /* update is only called so we create the engine.session */ - if (type->update) { - type->update(engine, re->main, engine->depsgraph); - } - - for (int i = 0; i < bake_images->size; i++) { - const BakeImage *image = bake_images->data + i; - - engine->bake.pixels = pixel_array + image->offset; - engine->bake.result = result + image->offset * depth; - engine->bake.width = image->width; - engine->bake.height = image->height; - engine->bake.depth = depth; - engine->bake.object_id = object_id; - - type->bake( - engine, engine->depsgraph, object, pass_type, pass_filter, image->width, image->height); - - memset(&engine->bake, 0, sizeof(engine->bake)); - } - - engine->depsgraph = NULL; - } - - engine->tile_x = 0; - engine->tile_y = 0; - engine->flag &= ~RE_ENGINE_RENDERING; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - - /* re->engine becomes zero if user changed active render engine during render */ - if (!persistent_data || !re->engine) { - RE_engine_free(engine); - re->engine = NULL; - } - - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); - - if (BKE_reports_contain(re->reports, RPT_ERROR)) { - G.is_break = true; - } - - return true; -} - -/* Render */ - -int RE_engine_render(Render *re, int do_all) -{ - RenderEngineType *type = RE_engines_find(re->r.engine); - RenderEngine *engine; - bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; - - /* verify if we can render */ - if (!type->render) { - return 0; - } - if ((re->r.scemode & R_BUTS_PREVIEW) && !(type->flag & RE_USE_PREVIEW)) { - return 0; - } - if (do_all && !(type->flag & RE_USE_POSTPROCESS)) { - return 0; - } - if (!do_all && (type->flag & RE_USE_POSTPROCESS)) { - return 0; - } - - /* Lock drawing in UI during data phase. */ - if (re->draw_lock) { - re->draw_lock(re->dlh, 1); - } - - /* update animation here so any render layer animation is applied before - * creating the render result */ - if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { - render_update_anim_renderdata(re, &re->scene->r, &re->scene->view_layers); - } - - /* create render result */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { - int savebuffers = RR_USE_MEM; - - if (re->result) { - render_result_free(re->result); - } - - if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { - savebuffers = RR_USE_EXR; - } - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); - } - BLI_rw_mutex_unlock(&re->resultmutex); - - if (re->result == NULL) { - /* Clear UI drawing locks. */ - if (re->draw_lock) { - re->draw_lock(re->dlh, 0); - } - /* Too small image is handled earlier, here it could only happen if - * there was no sufficient memory to allocate all passes. - */ - BKE_report(re->reports, RPT_ERROR, "Failed allocate render result, out of memory"); - G.is_break = true; - return 1; - } - - /* set render info */ - re->i.cfra = re->scene->r.cfra; - BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name)); - - /* render */ - engine = re->engine; - - if (!engine) { - engine = RE_engine_create(type); - re->engine = engine; - } - - engine->flag |= RE_ENGINE_RENDERING; - - /* TODO: actually link to a parent which shouldn't happen */ - engine->re = re; - - if (re->flag & R_ANIMATION) { - engine->flag |= RE_ENGINE_ANIMATION; - } - if (re->r.scemode & R_BUTS_PREVIEW) { - engine->flag |= RE_ENGINE_PREVIEW; - } - engine->camera_override = re->camera_override; - - engine->resolution_x = re->winx; - engine->resolution_y = re->winy; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - RE_parts_init(re); - engine->tile_x = re->partx; - engine->tile_y = re->party; - BLI_rw_mutex_unlock(&re->partsmutex); - - if (re->result->do_exr_tile) { - render_result_exr_file_begin(re, engine); - } - - /* Clear UI drawing locks. */ - if (re->draw_lock) { - re->draw_lock(re->dlh, 0); - } - - if (type->render) { - FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { - if (re->draw_lock) { - re->draw_lock(re->dlh, 1); - } - - ViewLayer *view_layer = BLI_findstring( - &re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)); - engine_depsgraph_init(engine, view_layer); - - if (type->update) { - type->update(engine, re->main, engine->depsgraph); - } - - if (re->draw_lock) { - re->draw_lock(re->dlh, 0); - } - - if (engine->type->flag & RE_USE_GPU_CONTEXT) { - DRW_render_context_enable(engine->re); - } - - type->render(engine, engine->depsgraph); - - if (engine->type->flag & RE_USE_GPU_CONTEXT) { - DRW_render_context_disable(engine->re); - } - - /* Grease pencil render over previous render result. - * - * NOTE: External engine might have been requested to free its - * dependency graph, which is only allowed if there is no grease - * pencil (pipeline is taking care of that). - */ - if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) { - DRW_render_gpencil(engine, engine->depsgraph); - } - - engine_depsgraph_free(engine); - - if (RE_engine_test_break(engine)) { - break; - } - } - FOREACH_VIEW_LAYER_TO_RENDER_END; - } - - engine->tile_x = 0; - engine->tile_y = 0; - engine->flag &= ~RE_ENGINE_RENDERING; - - render_result_free_list(&engine->fullresult, engine->fullresult.first); - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - - if (re->result->do_exr_tile) { - render_result_exr_file_end(re, engine); - } - - /* re->engine becomes zero if user changed active render engine during render */ - if (!persistent_data || !re->engine) { - RE_engine_free(engine); - re->engine = NULL; - } - - if (re->r.scemode & R_EXR_CACHE_FILE) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_cache_write(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); - - if (BKE_reports_contain(re->reports, RPT_ERROR)) { - G.is_break = true; - } - -#ifdef WITH_FREESTYLE - if (re->r.mode & R_EDGE_FRS) { - RE_RenderFreestyleExternal(re); - } -#endif - - return 1; -} - -void RE_engine_update_render_passes(struct RenderEngine *engine, - struct Scene *scene, - struct ViewLayer *view_layer, - update_render_passes_cb_t callback, - void *callback_data) -{ - if (!(scene && view_layer && engine && callback && engine->type->update_render_passes)) { - return; - } - - BLI_mutex_lock(&engine->update_render_passes_mutex); - - engine->update_render_passes_cb = callback; - engine->update_render_passes_data = callback_data; - engine->type->update_render_passes(engine, scene, view_layer); - engine->update_render_passes_cb = NULL; - engine->update_render_passes_data = NULL; - - BLI_mutex_unlock(&engine->update_render_passes_mutex); -} - -void RE_engine_register_pass(struct RenderEngine *engine, - struct Scene *scene, - struct ViewLayer *view_layer, - const char *name, - int channels, - const char *chanid, - eNodeSocketDatatype type) -{ - if (!(scene && view_layer && engine && engine->update_render_passes_cb)) { - return; - } - - engine->update_render_passes_cb( - engine->update_render_passes_data, scene, view_layer, name, channels, chanid, type); -} - -void RE_engine_free_blender_memory(RenderEngine *engine) -{ - /* Weak way to save memory, but not crash grease pencil. - * - * TODO(sergey): Find better solution for this. - */ - if (DRW_render_check_grease_pencil(engine->depsgraph)) { - return; - } - DEG_graph_free(engine->depsgraph); - engine->depsgraph = NULL; -} diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index ea18f151e1e..a4f68419c67 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -46,7 +46,7 @@ #include "RE_multires_bake.h" #include "RE_pipeline.h" -#include "RE_shader_ext.h" +#include "RE_texture.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -111,6 +111,11 @@ typedef struct { const int *orig_index_mp_to_orig; } MNormalBakeData; +typedef struct BakeImBufuserData { + float *displacement_buffer; + char *mask_buffer; +} BakeImBufuserData; + static void multiresbake_get_normal(const MResolvePixelData *data, float norm[], const int tri_num, diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 1bc6a056cda..3d19e5e6c15 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -86,7 +86,7 @@ #include "RE_engine.h" #include "RE_pipeline.h" -#include "RE_render_ext.h" +#include "RE_texture.h" #include "SEQ_sequencer.h" diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index 26d9f12c91e..cd34226920d 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -46,8 +46,7 @@ #include "BKE_image.h" -#include "RE_render_ext.h" -#include "RE_shader_ext.h" +#include "RE_texture.h" #include "render_types.h" #include "texture_common.h" diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index 0e62eea5b11..e2568e0a013 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -56,8 +56,7 @@ #include "render_types.h" #include "texture_common.h" -#include "RE_render_ext.h" -#include "RE_shader_ext.h" +#include "RE_texture.h" static ThreadMutex sample_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index 7f93addb76d..a98f29a705d 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -57,8 +57,7 @@ #include "render_types.h" #include "texture_common.h" -#include "RE_render_ext.h" -#include "RE_shader_ext.h" +#include "RE_texture.h" static RNG_THREAD_ARRAY *random_tex_array; -- cgit v1.2.3