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:
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)