Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDalai Felinto <dfelinto@gmail.com>2014-01-03 01:05:07 +0400
committerDalai Felinto <dfelinto@gmail.com>2014-05-03 04:19:08 +0400
commit97641a0ec946005d9a042f075697109c6590a28d (patch)
tree7661e462d6426298453d1f5ddd6b7e2ffb74ed65 /source/blender/render
parent3312b20ac8c8a5e45d87c5c1f59ce0907350d398 (diff)
Bake API - bpy.ops.object.bake()
New operator that can calls a bake function to the current render engine when available. This commit provides no feature for the users, but allows external engines to be accessed by the operator and be integrated with the baking api. 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. The Blender Internal (and multires) system is still running independent, but we eventually will pipe it through the API as well. Cycles baking will come next as a separated commit Python Operator: ---------------- The operator can be called with some arguments, or a user interface can be created for it. In that case the arguments can be ommited and the interface can expose the settings from bpy.context.scene.render.bake bpy.ops.object.bake(type='COMBINED', filepath="", width=512, height=512, margin=16, use_selected_to_active=False, cage_extrusion=0, cage="", normal_space='TANGENT', normal_r='POS_X', normal_g='POS_Y', normal_b='POS_Z', save_mode='INTERNAL', use_clear=False, use_split_materials=False, use_automatic_name=False) Note: external save mode is currently disabled. Supported Features: ------------------ * Margin - Baked result is extended this many pixels beyond the border of each UV "island," to soften seams in the texture. * Selected to Active - bake shading on the surface of selected object to the active object. The rays are cast from the lowpoly object inwards towards the highpoly object. If the highpoly object is not entirely involved by the lowpoly object, you can tweak the rays start point with Cage Extrusion. For even more control of the cage you can use a Cage object. * Cage Extrusion - distance to use for the inward ray cast when using selected to active * Custom Cage - object to use as cage (instead of the lowpoly object). * Normal swizzle - change the axis that gets mapped to RGB * Normal space - save as tangent or object normal spaces Supported Passes: ----------------- Any pass that is supported by Blender renderlayer system. Though it's up to the external engine to provide a valid enum with its supported passes. Normal passes get a special treatment since we post-process them to converted and "swizzled" Development Notes for External Engines: --------------------------------------- (read them in bake_api.c) * For a complete implementation example look at the Cycles Bake commit (next). Review: D421 Reviewed by: Campbell Barton, Brecht van Lommel, Sergey Sharybin, Thomas Dinge Normal map pipeline "consulting" by Andy Davies (metalliandy) Original design by Brecht van Lommel. The entire commit history can be found on the branch: bake-cycles
Diffstat (limited to 'source/blender/render')
-rw-r--r--source/blender/render/CMakeLists.txt2
-rw-r--r--source/blender/render/extern/include/RE_bake.h105
-rw-r--r--source/blender/render/extern/include/RE_engine.h4
-rw-r--r--source/blender/render/intern/source/bake.c2
-rw-r--r--source/blender/render/intern/source/bake_api.c829
-rw-r--r--source/blender/render/intern/source/external_engine.c83
6 files changed, 1023 insertions, 2 deletions
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index b56c209ca4a..8e326e770fc 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -53,6 +53,7 @@ set(SRC
intern/raytrace/rayobject_rtbuild.cpp
intern/raytrace/rayobject_vbvh.cpp
intern/source/bake.c
+ intern/source/bake_api.c
intern/source/convertblender.c
intern/source/envmap.c
intern/source/external_engine.c
@@ -82,6 +83,7 @@ set(SRC
intern/source/zbuf.c
extern/include/RE_engine.h
+ extern/include/RE_bake.h
extern/include/RE_multires_bake.h
extern/include/RE_pipeline.h
extern/include/RE_render_ext.h
diff --git a/source/blender/render/extern/include/RE_bake.h b/source/blender/render/extern/include/RE_bake.h
new file mode 100644
index 00000000000..d59819c8ef4
--- /dev/null
+++ b/source/blender/render/extern/include/RE_bake.h
@@ -0,0 +1,105 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2010 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s):
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file RE_bake.h
+ * \ingroup render
+ */
+
+#ifndef __RE_BAKE_H__
+#define __RE_BAKE_H__
+
+struct Render;
+struct Mesh;
+
+typedef struct BakeImage {
+ struct Image *image;
+ int width;
+ int height;
+ int offset;
+} BakeImage;
+
+typedef struct BakeImages {
+ BakeImage *data; /* all the images of an object */
+ int *lookup; /* lookup table from Material to BakeImage */
+ int size;
+} BakeImages;
+
+typedef struct BakePixel {
+ int primitive_id;
+ float uv[2];
+ float du_dx, du_dy;
+ float dv_dx, dv_dy;
+} BakePixel;
+
+typedef struct BakeHighPolyData {
+ struct BakePixel *pixel_array;
+ struct Object *ob;
+ struct ModifierData *tri_mod;
+ struct Mesh *me;
+ char restrict_flag;
+ float mat_lowtohigh[4][4];
+} BakeHighPolyData;
+
+/* external_engine.c */
+bool RE_bake_has_engine(struct Render *re);
+
+bool RE_bake_engine(
+ struct Render *re, struct Object *object, const BakePixel pixel_array[],
+ const int num_pixels, const int depth, const ScenePassType pass_type, float result[]);
+
+/* bake.c */
+int RE_pass_depth(const ScenePassType pass_type);
+bool RE_bake_internal(
+ struct Render *re, struct Object *object, const BakePixel pixel_array[],
+ const int num_pixels, const int depth, const ScenePassType pass_type, float result[]);
+
+void RE_bake_pixels_populate_from_objects(
+ struct Mesh *me_low, BakePixel pixel_array_from[],
+ BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels,
+ const float cage_extrusion);
+
+void RE_bake_pixels_populate(
+ struct Mesh *me, struct BakePixel *pixel_array,
+ const int num_pixels, const struct BakeImages *bake_images);
+
+void RE_bake_mask_fill(const BakePixel pixel_array[], const int num_pixels, char *mask);
+
+void RE_bake_margin(struct ImBuf *ibuf, char *mask, const int margin);
+
+void RE_bake_normal_world_to_object(
+ const BakePixel pixel_array[], const int num_pixels, const int depth, float result[],
+ struct Object *ob, const BakeNormalSwizzle normal_swizzle[3]);
+void RE_bake_normal_world_to_tangent(
+ const BakePixel pixel_array[], const int num_pixels, const int depth, float result[],
+ struct Mesh *me, const BakeNormalSwizzle normal_swizzle[3]);
+void RE_bake_normal_world_to_world(
+ const BakePixel pixel_array[], const int num_pixels, const int depth, float result[],
+ const BakeNormalSwizzle normal_swizzle[3]);
+
+void RE_bake_ibuf_clear(struct BakeImages *bake_images, const bool is_tangent);
+
+#endif /* __RE_BAKE_H__ */
diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h
index bd976e6c14a..2c6492b5c5a 100644
--- a/source/blender/render/extern/include/RE_engine.h
+++ b/source/blender/render/extern/include/RE_engine.h
@@ -35,6 +35,7 @@
#include "DNA_listBase.h"
#include "DNA_scene_types.h"
#include "RNA_types.h"
+#include "RE_bake.h"
struct bNode;
struct bNodeTree;
@@ -47,6 +48,7 @@ struct RenderLayer;
struct RenderResult;
struct ReportList;
struct Scene;
+struct BakePixel;
/* External Engine */
@@ -85,6 +87,7 @@ typedef struct RenderEngineType {
void (*update)(struct RenderEngine *engine, struct Main *bmain, struct Scene *scene);
void (*render)(struct RenderEngine *engine, struct Scene *scene);
+ void (*bake)(struct RenderEngine *engine, struct Scene *scene, struct Object *object, const int pass_type, const struct BakePixel *pixel_array, const int num_pixels, const int depth, void *result);
void (*view_update)(struct RenderEngine *engine, const struct bContext *context);
void (*view_draw)(struct RenderEngine *engine, const struct bContext *context);
@@ -153,6 +156,7 @@ RenderEngineType *RE_engines_find(const char *idname);
void RE_engine_get_current_tiles(struct Render *re, int *total_tiles_r, rcti **tiles_r);
struct RenderData *RE_engine_get_render_data(struct Render *re);
+void RE_bake_engine_set_engine_parameters(struct Render *re, struct Main *bmain, struct Scene *scene);
#endif /* __RE_ENGINE_H__ */
diff --git a/source/blender/render/intern/source/bake.c b/source/blender/render/intern/source/bake.c
index f3cddf0c7cb..f2793a9bc5b 100644
--- a/source/blender/render/intern/source/bake.c
+++ b/source/blender/render/intern/source/bake.c
@@ -54,6 +54,8 @@
#include "IMB_imbuf.h"
#include "IMB_colormanagement.h"
+#include "RE_bake.h"
+
/* local include */
#include "rayintersection.h"
#include "rayobject.h"
diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c
new file mode 100644
index 00000000000..7ad07e2aaf4
--- /dev/null
+++ b/source/blender/render/intern/source/bake_api.c
@@ -0,0 +1,829 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/bake_api.c
+ * \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, 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", ...)
+ * - 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:
+
+ * struct BakePixel {
+ * int primitive_id;
+ * float u, v;
+ * float dudx, dudy;
+ * float dvdx, dvdy;
+ * };
+ *
+ * In python you have access to:
+ * - primitive_id, u, v, 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 "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "DNA_mesh_types.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_image.h"
+#include "BKE_node.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "RE_bake.h"
+
+/* local include */
+#include "render_types.h"
+#include "zbuf.h"
+
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+
+typedef struct BakeDataZSpan {
+ BakePixel *pixel_array;
+ int primitive_id;
+ BakeImage *bk_image;
+ ZSpan *zspan;
+} 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 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 int offset = bd->bk_image->offset;
+ const int i = offset + y * width + x;
+
+ pixel = &bd->pixel_array[i];
+ pixel->primitive_id = bd->primitive_id;
+
+ copy_v2_fl2(pixel->uv, u, v);
+
+ pixel->du_dx =
+ pixel->du_dy =
+ pixel->dv_dx =
+ pixel->dv_dy =
+ 0.0f;
+}
+
+void RE_bake_mask_fill(const BakePixel pixel_array[], const int num_pixels, char *mask)
+{
+ int 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.
+ */
+static void calc_point_from_barycentric(
+ TriTessFace *triangles, int primitive_id, float u, float v, float cage_extrusion,
+ float r_co[3], float r_dir[3])
+{
+ float data[3][3];
+ float coord[3];
+ float dir[3];
+ float cage[3];
+
+ TriTessFace *triangle = &triangles[primitive_id];
+
+ 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);
+
+ 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_v3(cage, dir);
+ mul_v3_fl(cage, cage_extrusion);
+
+ add_v3_v3(coord, cage);
+
+ normalize_v3_v3(dir, dir);
+ mul_v3_fl(dir, -1.0f);
+
+ copy_v3_v3(r_co, coord);
+ copy_v3_v3(r_dir, dir);
+}
+
+/**
+ * This function returns the barycentric u,v of a face for a coordinate. The face is defined by its index.
+ */
+static void calc_barycentric_from_point(
+ TriTessFace *triangles, const int index, const float co[3],
+ int *r_primitive_id, float r_uv[2])
+{
+ TriTessFace *triangle = &triangles[index];
+ resolve_tri_uv_v3(r_uv, co,
+ triangle->mverts[0]->co,
+ triangle->mverts[1]->co,
+ triangle->mverts[2]->co);
+ *r_primitive_id = index;
+}
+
+/**
+ * This function populates pixel_array and returns TRUE if things are correct
+ */
+static bool cast_ray_highpoly(
+ BVHTreeFromMesh *treeData, TriTessFace *triangles[], BakeHighPolyData *highpoly,
+ float const co_low[3], const float dir[3], const int pixel_id, const int tot_highpoly)
+{
+ int i;
+ int primitive_id = -1;
+ float uv[2];
+ int hit_mesh = -1;
+ float 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];
+ hits[i].index = -1;
+ /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */
+ hits[i].dist = 10000.0f;
+
+ copy_v3_v3(co_high, co_low);
+
+ /* transform the ray from the lowpoly to the highpoly space */
+ mul_m4_v3(highpoly[i].mat_lowtohigh, co_high);
+
+ /* cast ray */
+ BLI_bvhtree_ray_cast(treeData[i].tree, co_high, dir, 0.0f, &hits[i], treeData[i].raycast_callback, &treeData[i]);
+
+ if (hits[i].index != -1) {
+ /* cull backface */
+ const float dot = dot_v3v3(dir, hits[i].no);
+ if (dot < 0.0f) {
+ if (hits[i].dist < hit_distance) {
+ hit_mesh = i;
+ hit_distance = hits[i].dist;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < tot_highpoly; i++) {
+ if (hit_mesh == i) {
+ calc_barycentric_from_point(triangles[i], hits[i].index, hits[i].co, &primitive_id, uv);
+ highpoly[i].pixel_array[pixel_id].primitive_id = primitive_id;
+ copy_v2_v2(highpoly[i].pixel_array[pixel_id].uv, uv);
+ }
+ else {
+ highpoly[i].pixel_array[pixel_id].primitive_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 void mesh_calc_tri_tessface(
+ TriTessFace *triangles, Mesh *me, bool tangent, DerivedMesh *dm)
+{
+ int i;
+ int p_id;
+ MFace *mface;
+ MVert *mvert;
+ TSpace *tspace;
+ float *precomputed_normals;
+ bool calculate_normal;
+
+ mface = CustomData_get_layer(&me->fdata, CD_MFACE);
+ mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
+
+ if (tangent) {
+ DM_ensure_normals(dm);
+ DM_add_tangent_layer(dm);
+
+ precomputed_normals = dm->getTessFaceDataArray(dm, CD_NORMAL);
+ calculate_normal = precomputed_normals ? false : true;
+
+ //mface = dm->getTessFaceArray(dm);
+ //mvert = dm->getVertArray(dm);
+
+ tspace = dm->getTessFaceDataArray(dm, CD_TANGENT);
+ BLI_assert(tspace);
+ }
+
+ p_id = -1;
+ for (i = 0; i < me->totface; i++) {
+ MFace *mf = &mface[i];
+ TSpace *ts = &tspace[i * 4];
+
+ p_id++;
+
+ triangles[p_id].mverts[0] = &mvert[mf->v1];
+ triangles[p_id].mverts[1] = &mvert[mf->v2];
+ triangles[p_id].mverts[2] = &mvert[mf->v3];
+ triangles[p_id].is_smooth = (mf->flag & ME_SMOOTH) != 0;
+
+ if (tangent) {
+ triangles[p_id].tspace[0] = &ts[0];
+ triangles[p_id].tspace[1] = &ts[1];
+ triangles[p_id].tspace[2] = &ts[2];
+
+ if (calculate_normal) {
+ if (mf->v4 != 0) {
+ normal_quad_v3(triangles[p_id].normal,
+ mvert[mf->v1].co,
+ mvert[mf->v2].co,
+ mvert[mf->v3].co,
+ mvert[mf->v4].co);
+ }
+ else {
+ normal_tri_v3(triangles[p_id].normal,
+ triangles[p_id].mverts[0]->co,
+ triangles[p_id].mverts[1]->co,
+ triangles[p_id].mverts[2]->co);
+ }
+ }
+ else {
+ copy_v3_v3(triangles[p_id].normal, &precomputed_normals[3 * i]);
+ }
+ }
+
+ /* 4 vertices in the face */
+ if (mf->v4 != 0) {
+ p_id++;
+
+ triangles[p_id].mverts[0] = &mvert[mf->v1];
+ triangles[p_id].mverts[1] = &mvert[mf->v3];
+ triangles[p_id].mverts[2] = &mvert[mf->v4];
+ triangles[p_id].is_smooth = (mf->flag & ME_SMOOTH) != 0;
+
+ if (tangent) {
+ triangles[p_id].tspace[0] = &ts[0];
+ triangles[p_id].tspace[1] = &ts[2];
+ triangles[p_id].tspace[2] = &ts[3];
+
+ /* same normal as the other "triangle" */
+ copy_v3_v3(triangles[p_id].normal, triangles[p_id - 1].normal);
+ }
+ }
+ }
+
+ BLI_assert(p_id < me->totface * 2);
+}
+
+void RE_bake_pixels_populate_from_objects(
+ struct Mesh *me_low, BakePixel pixel_array_from[],
+ BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels,
+ const float cage_extrusion)
+{
+ int i;
+ int primitive_id;
+ float u, v;
+
+ DerivedMesh **dm_highpoly;
+ BVHTreeFromMesh *treeData;
+
+ /* Note: all coordinates are in local space */
+ TriTessFace *tris_low;
+ TriTessFace **tris_high;
+
+ /* assume all lowpoly tessfaces can be quads */
+ tris_low = MEM_callocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Lowpoly Mesh");
+ tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array");
+
+ /* assume all highpoly tessfaces are triangles */
+ dm_highpoly = MEM_callocN(sizeof(DerivedMesh *) * tot_highpoly, "Highpoly Derived Meshes");
+ treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees");
+
+ mesh_calc_tri_tessface(tris_low, me_low, false, NULL);
+
+ for (i = 0; i < tot_highpoly; i++) {
+ tris_high[i] = MEM_callocN(sizeof(TriTessFace) * highpoly[i].me->totface, "MVerts Highpoly Mesh");
+ mesh_calc_tri_tessface(tris_high[i], highpoly[i].me, false, NULL);
+
+ dm_highpoly[i] = CDDM_from_mesh(highpoly[i].me);
+
+ /* Create a bvh-tree for each highpoly object */
+ bvhtree_from_mesh_faces(&treeData[i], dm_highpoly[i], 0.0, 2, 6);
+
+ if (&treeData[i].tree == NULL) {
+ printf("Baking: Out of memory\n");
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; i < num_pixels; i++) {
+ float co[3];
+ float dir[3];
+
+ primitive_id = pixel_array_from[i].primitive_id;
+
+ if (primitive_id == -1) {
+ int j;
+ for (j = 0; j < tot_highpoly; j++) {
+ highpoly[j].pixel_array[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 */
+ calc_point_from_barycentric(tris_low, primitive_id, u, v, cage_extrusion, co, dir);
+
+ /* cast ray */
+ if (!cast_ray_highpoly(treeData, tris_high, highpoly, co, dir, i, tot_highpoly)) {
+ /* 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]);
+ dm_highpoly[i]->release(dm_highpoly[i]);
+ MEM_freeN(tris_high[i]);
+ }
+
+ MEM_freeN(tris_low);
+ MEM_freeN(tris_high);
+ MEM_freeN(treeData);
+ MEM_freeN(dm_highpoly);
+}
+
+void RE_bake_pixels_populate(
+ Mesh *me, BakePixel pixel_array[],
+ const int num_pixels, const BakeImages *bake_images)
+{
+ BakeDataZSpan bd;
+ int i, a;
+ int p_id;
+
+ MTFace *mtface;
+ MFace *mface;
+
+ /* we can't bake in edit mode */
+ if (me->edit_btmesh)
+ return;
+
+ 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 (i = 0; i < num_pixels; i++) {
+ pixel_array[i].primitive_id = -1;
+ }
+
+ for (i = 0; i < bake_images->size; i++) {
+ zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height, R.clipcrop);
+ }
+
+ mtface = CustomData_get_layer(&me->fdata, CD_MTFACE);
+ mface = CustomData_get_layer(&me->fdata, CD_MFACE);
+
+ if (mtface == NULL)
+ return;
+
+ p_id = -1;
+ for (i = 0; i < me->totface; i++) {
+ float vec[4][2];
+ MTFace *mtf = &mtface[i];
+ MFace *mf = &mface[i];
+ int mat_nr = mf->mat_nr;
+ int image_id = bake_images->lookup[mat_nr];
+
+ bd.bk_image = &bake_images->data[image_id];
+ bd.primitive_id = ++p_id;
+
+ for (a = 0; a < 4; a++) {
+ /* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests
+ * where a pixel gets in between 2 faces or the middle of a quad,
+ * camera aligned quads also have this problem but they are less common.
+ * Add a small offset to the UVs, fixes bug #18685 - Campbell */
+ vec[a][0] = mtf->uv[a][0] * (float)bd.bk_image->width - (0.5f + 0.001f);
+ vec[a][1] = mtf->uv[a][1] * (float)bd.bk_image->height - (0.5f + 0.002f);
+ }
+
+ zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel);
+
+ /* 4 vertices in the face */
+ if (mf->v4 != 0) {
+ bd.primitive_id = ++p_id;
+ zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[2], vec[3], store_bake_pixel);
+ }
+ }
+
+ for (i = 0; i < bake_images->size; i++) {
+ zbuf_free_span(&bd.zspan[i]);
+ }
+ 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 BakeNormalSwizzle 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 int num_pixels, const int depth,
+ float result[], Mesh *me, const BakeNormalSwizzle normal_swizzle[3])
+{
+ int i;
+
+ TriTessFace *triangles;
+
+ DerivedMesh *dm = CDDM_from_mesh(me);
+
+ triangles = MEM_callocN(sizeof(TriTessFace) * (me->totface * 2), "MVerts Mesh");
+ mesh_calc_tri_tessface(triangles, me, true, dm);
+
+ 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];
+
+ int offset;
+ float nor[3]; /* texture normal */
+
+ bool is_smooth;
+
+ int primitive_id = pixel_array[i].primitive_id;
+
+ offset = i * depth;
+
+ if (primitive_id == -1) {
+ 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)
+ normal_short_to_float_v3(normals[j], triangle->mverts[j]->no);
+ else
+ normal[j] = triangle->normal[j];
+
+ 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);
+
+ /* 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]);
+
+ 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 (dm)
+ dm->release(dm);
+}
+
+void RE_bake_normal_world_to_object(
+ const BakePixel pixel_array[], const int num_pixels, const int depth,
+ float result[], struct Object *ob, const BakeNormalSwizzle normal_swizzle[3])
+{
+ int i;
+ float iobmat[4][4];
+
+ invert_m4_m4(iobmat, ob->obmat);
+
+ for (i = 0; i < num_pixels; i++) {
+ int offset;
+ float nor[3];
+
+ if (pixel_array[i].primitive_id == -1)
+ continue;
+
+ offset = i * depth;
+ normal_uncompress(nor, &result[offset]);
+
+ mul_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 int num_pixels, const int depth,
+ float result[], const BakeNormalSwizzle normal_swizzle[3])
+{
+ int i;
+
+ for (i = 0; i < num_pixels; i++) {
+ int 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(BakeImages *bake_images, const bool is_tangent)
+{
+ ImBuf *ibuf;
+ void *lock;
+ Image *image;
+ int i;
+
+ 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};
+
+ for (i = 0; i < bake_images->size; i ++) {
+ image = bake_images->data[i].image;
+
+ 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);
+ }
+}
+
+/* ************************************************************* */
+
+/**
+ * not the real UV, but the internal per-face UV instead
+ * I'm using it to test if everything is correct */
+static bool bake_uv(const BakePixel pixel_array[], const int num_pixels, const int depth, float result[])
+{
+ int i;
+
+ for (i=0; i < num_pixels; i++) {
+ int offset = i * depth;
+ copy_v2_v2(&result[offset], pixel_array[i].uv);
+ }
+
+ return true;
+}
+
+bool RE_bake_internal(
+ Render *UNUSED(re), Object *UNUSED(object), const BakePixel pixel_array[],
+ const int num_pixels, const int depth, const ScenePassType pass_type, float result[])
+{
+ switch (pass_type) {
+ case SCE_PASS_UV:
+ {
+ return bake_uv(pixel_array, num_pixels, depth, result);
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+int RE_pass_depth(const ScenePassType 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_RGBA:
+ {
+ return 4;
+ }
+ case SCE_PASS_COMBINED:
+ case SCE_PASS_DIFFUSE:
+ case SCE_PASS_SPEC:
+ case SCE_PASS_SHADOW:
+ case SCE_PASS_REFLECT:
+ case SCE_PASS_NORMAL:
+ case SCE_PASS_VECTOR:
+ case SCE_PASS_REFRACT:
+ case SCE_PASS_INDEXOB: /* XXX double check */
+ case SCE_PASS_INDIRECT:
+ 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/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index f5901370c61..e8751210540 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -57,6 +57,7 @@
#include "RE_engine.h"
#include "RE_pipeline.h"
+#include "RE_bake.h"
#include "initrender.h"
#include "render_types.h"
@@ -67,7 +68,7 @@
static RenderEngineType internal_render_type = {
NULL, NULL,
"BLENDER_RENDER", N_("Blender Render"), RE_INTERNAL,
- NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
{NULL, NULL, NULL}
};
@@ -76,7 +77,7 @@ static RenderEngineType internal_render_type = {
static RenderEngineType internal_game_type = {
NULL, NULL,
"BLENDER_GAME", N_("Blender Game"), RE_INTERNAL | RE_GAME,
- NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
{NULL, NULL, NULL}
};
@@ -402,6 +403,84 @@ RenderData *RE_engine_get_render_data(Render *re)
return &re->r;
}
+/* Bake */
+void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene)
+{
+ re->scene = scene;
+ re->main = bmain;
+ re->r = scene->r;
+
+ /* prevent crash when freeing the scene
+ but it potentially leaves unfreed memory blocks
+ not sure how to fix this yet -- dfelinto */
+ re->r.layers.first = re->r.layers.last = NULL;
+}
+
+bool RE_bake_has_engine(Render *re)
+{
+ RenderEngineType *type = RE_engines_find(re->r.engine);
+ return (bool)(type->bake);
+}
+
+bool RE_bake_engine(
+ Render *re, Object *object, const BakePixel pixel_array[],
+ const int num_pixels, const int depth,
+ const ScenePassType pass_type, float result[])
+{
+ RenderEngineType *type = RE_engines_find(re->r.engine);
+ RenderEngine *engine;
+ int persistent_data = re->r.mode & R_PERSISTENT_DATA;
+
+ /* 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);
+ re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0;
+
+ /* 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;
+
+ RE_parts_init(re, false);
+ engine->tile_x = re->partx;
+ engine->tile_y = re->party;
+
+ /* update is only called so we create the engine.session */
+ if (type->update)
+ type->update(engine, re->main, re->scene);
+
+ if (type->bake)
+ type->bake(engine, re->scene, object, pass_type, pixel_array, num_pixels, depth, result);
+
+ engine->tile_x = 0;
+ engine->tile_y = 0;
+ engine->flag &= ~RE_ENGINE_RENDERING;
+
+ /* 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);
+
+ if (BKE_reports_contain(re->reports, RPT_ERROR))
+ G.is_break = true;
+
+ return true;
+}
+
/* Render */
static bool render_layer_exclude_animated(Scene *scene, SceneRenderLayer *srl)