Bake API - bpy.ops.object.bake()
authorDalai Felinto <dfelinto@gmail.com>
Thu, 2 Jan 2014 21:05:07 +0000 (19:05 -0200)
committerDalai Felinto <dfelinto@gmail.com>
Sat, 3 May 2014 00:19:08 +0000 (21:19 -0300)
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

21 files changed:
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/mesh.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/versioning_270.c
source/blender/editors/object/CMakeLists.txt
source/blender/editors/object/object_bake.c
source/blender/editors/object/object_bake_api.c [new file with mode: 0644]
source/blender/editors/object/object_intern.h
source/blender/editors/object/object_ops.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_render.c
source/blender/makesrna/intern/rna_scene.c
source/blender/render/CMakeLists.txt
source/blender/render/extern/include/RE_bake.h [new file with mode: 0644]
source/blender/render/extern/include/RE_engine.h
source/blender/render/intern/source/bake.c
source/blender/render/intern/source/bake_api.c [new file with mode: 0644]
source/blender/render/intern/source/external_engine.c
source/blender/windowmanager/WM_api.h

index 4b346b201baa304977424998e370cb7f172a5264..587dea5095ca853357d0422af8ce981b768e161c 100644 (file)
@@ -127,6 +127,9 @@ bool BKE_mesh_uv_cdlayer_rename(struct Mesh *me, const char *old_name, const cha
 
 float (*BKE_mesh_vertexCos_get(struct Mesh *me, int *r_numVerts))[3];
 
+struct Mesh *BKE_mesh_new_from_object(struct Main *bmain, struct Scene *sce, struct Object *ob,
+                                      int apply_modifiers, int settings, int calc_tessface, int calc_undeformed);
+
 /* vertex level transformations & checks (no derived mesh) */
 
 bool BKE_mesh_minmax(struct Mesh *me, float r_min[3], float r_max[3]);
index f2ff350178f84114140ed81795ee1b568e63a5c1..7b415cff5554ef88ea8153c9b467a14374c16e2f 100644 (file)
@@ -53,6 +53,8 @@
 #include "BKE_modifier.h"
 #include "BKE_multires.h"
 #include "BKE_key.h"
+#include "BKE_mball.h"
+#include "BKE_depsgraph.h"
 /* these 2 are only used by conversion functions */
 #include "BKE_curve.h"
 /* -- */
@@ -2085,3 +2087,220 @@ void BKE_mesh_mselect_active_set(Mesh *me, int index, int type)
        BLI_assert((me->mselect[me->totselect - 1].index == index) &&
                   (me->mselect[me->totselect - 1].type  == type));
 }
+
+/* settings: 1 - preview, 2 - render */
+Mesh *BKE_mesh_new_from_object(
+        Main *bmain, Scene *sce, Object *ob,
+        int apply_modifiers, int settings, int calc_tessface, int calc_undeformed)
+{
+       Mesh *tmpmesh;
+       Curve *tmpcu = NULL, *copycu;
+       Object *tmpobj = NULL;
+       int render = settings == eModifierMode_Render, i;
+       int cage = !apply_modifiers;
+
+       /* perform the mesh extraction based on type */
+       switch (ob->type) {
+               case OB_FONT:
+               case OB_CURVE:
+               case OB_SURF:
+               {
+                       ListBase dispbase = {NULL, NULL};
+                       DerivedMesh *derivedFinal = NULL;
+                       int uv_from_orco;
+
+                       /* copies object and modifiers (but not the data) */
+                       tmpobj = BKE_object_copy_ex(bmain, ob, true);
+                       tmpcu = (Curve *)tmpobj->data;
+                       tmpcu->id.us--;
+
+                       /* if getting the original caged mesh, delete object modifiers */
+                       if (cage)
+                               BKE_object_free_modifiers(tmpobj);
+
+                       /* copies the data */
+                       copycu = tmpobj->data = BKE_curve_copy((Curve *) ob->data);
+
+                       /* temporarily set edit so we get updates from edit mode, but
+                        * also because for text datablocks copying it while in edit
+                        * mode gives invalid data structures */
+                       copycu->editfont = tmpcu->editfont;
+                       copycu->editnurb = tmpcu->editnurb;
+
+                       /* get updated display list, and convert to a mesh */
+                       BKE_displist_make_curveTypes_forRender(sce, tmpobj, &dispbase, &derivedFinal, false, render);
+
+                       copycu->editfont = NULL;
+                       copycu->editnurb = NULL;
+
+                       tmpobj->derivedFinal = derivedFinal;
+
+                       /* convert object type to mesh */
+                       uv_from_orco = (tmpcu->flag & CU_UV_ORCO) != 0;
+                       BKE_mesh_from_nurbs_displist(tmpobj, &dispbase, uv_from_orco);
+
+                       tmpmesh = tmpobj->data;
+
+                       BKE_displist_free(&dispbase);
+
+                       /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked.
+                        * if it didn't the curve did not have any segments or otherwise 
+                        * would have generated an empty mesh */
+                       if (tmpobj->type != OB_MESH) {
+                               BKE_libblock_free_us(G.main, tmpobj);
+                               return NULL;
+                       }
+
+                       BKE_mesh_texspace_copy_from_object(tmpmesh, ob);
+
+                       BKE_libblock_free_us(bmain, tmpobj);
+                       break;
+               }
+
+               case OB_MBALL:
+               {
+                       /* metaballs don't have modifiers, so just convert to mesh */
+                       Object *basis_ob = BKE_mball_basis_find(sce, ob);
+                       /* todo, re-generatre for render-res */
+                       /* metaball_polygonize(scene, ob) */
+
+                       if (ob != basis_ob)
+                               return NULL;  /* only do basis metaball */
+
+                       tmpmesh = BKE_mesh_add(bmain, "Mesh");
+                       /* BKE_mesh_add gives us a user count we don't need */
+                       tmpmesh->id.us--;
+
+                       if (render) {
+                               ListBase disp = {NULL, NULL};
+                               /* TODO(sergey): This is gonna to work for until EvaluationContext
+                                *               only contains for_render flag. As soon as CoW is
+                                *               implemented, this is to be rethinked.
+                                */
+                               EvaluationContext eval_ctx = {0};
+                               eval_ctx.for_render = render;
+                               BKE_displist_make_mball_forRender(&eval_ctx, sce, ob, &disp);
+                               BKE_mesh_from_metaball(&disp, tmpmesh);
+                               BKE_displist_free(&disp);
+                       }
+                       else {
+                               ListBase disp = {NULL, NULL};
+                               if (ob->curve_cache) {
+                                       disp = ob->curve_cache->disp;
+                               }
+                               BKE_mesh_from_metaball(&disp, tmpmesh);
+                       }
+
+                       BKE_mesh_texspace_copy_from_object(tmpmesh, ob);
+
+                       break;
+
+               }
+               case OB_MESH:
+                       /* copies object and modifiers (but not the data) */
+                       if (cage) {
+                               /* copies the data */
+                               tmpmesh = BKE_mesh_copy_ex(bmain, ob->data);
+                               /* if not getting the original caged mesh, get final derived mesh */
+                       }
+                       else {
+                               /* Make a dummy mesh, saves copying */
+                               DerivedMesh *dm;
+                               /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */
+                               CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter,
+                                                                * for example, needs CD_MASK_MDEFORMVERT */
+
+                               if (calc_undeformed)
+                                       mask |= CD_MASK_ORCO;
+
+                               /* Write the display mesh into the dummy mesh */
+                               if (render)
+                                       dm = mesh_create_derived_render(sce, ob, mask);
+                               else
+                                       dm = mesh_create_derived_view(sce, ob, mask);
+
+                               tmpmesh = BKE_mesh_add(bmain, "Mesh");
+                               DM_to_mesh(dm, tmpmesh, ob, mask);
+                               dm->release(dm);
+                       }
+
+                       /* BKE_mesh_add/copy gives us a user count we don't need */
+                       tmpmesh->id.us--;
+
+                       break;
+               default:
+                       /* "Object does not have geometry data") */
+                       return NULL;
+       }
+
+       /* Copy materials to new mesh */
+       switch (ob->type) {
+               case OB_SURF:
+               case OB_FONT:
+               case OB_CURVE:
+                       tmpmesh->totcol = tmpcu->totcol;
+
+                       /* free old material list (if it exists) and adjust user counts */
+                       if (tmpcu->mat) {
+                               for (i = tmpcu->totcol; i-- > 0; ) {
+                                       /* are we an object material or data based? */
+
+                                       tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : tmpcu->mat[i];
+
+                                       if (tmpmesh->mat[i]) {
+                                               tmpmesh->mat[i]->id.us++;
+                                       }
+                               }
+                       }
+                       break;
+
+#if 0
+               /* Crashes when assigning the new material, not sure why */
+               case OB_MBALL:
+                       tmpmb = (MetaBall *)ob->data;
+                       tmpmesh->totcol = tmpmb->totcol;
+
+                       /* free old material list (if it exists) and adjust user counts */
+                       if (tmpmb->mat) {
+                               for (i = tmpmb->totcol; i-- > 0; ) {
+                                       tmpmesh->mat[i] = tmpmb->mat[i]; /* CRASH HERE ??? */
+                                       if (tmpmesh->mat[i]) {
+                                               tmpmb->mat[i]->id.us++;
+                                       }
+                               }
+                       }
+                       break;
+#endif
+
+               case OB_MESH:
+                       if (!cage) {
+                               Mesh *origmesh = ob->data;
+                               tmpmesh->flag = origmesh->flag;
+                               tmpmesh->mat = MEM_dupallocN(origmesh->mat);
+                               tmpmesh->totcol = origmesh->totcol;
+                               tmpmesh->smoothresh = origmesh->smoothresh;
+                               if (origmesh->mat) {
+                                       for (i = origmesh->totcol; i-- > 0; ) {
+                                               /* are we an object material or data based? */
+                                               tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : origmesh->mat[i];
+
+                                               if (tmpmesh->mat[i]) {
+                                                       tmpmesh->mat[i]->id.us++;
+                                               }
+                                       }
+                               }
+                       }
+                       break;
+       } /* end copy materials */
+
+       if (calc_tessface) {
+               /* cycles and exporters rely on this still */
+               BKE_mesh_tessface_ensure(tmpmesh);
+       }
+
+       /* make sure materials get updated in objects */
+       test_object_materials(bmain, &tmpmesh->id);
+
+       return tmpmesh;
+}
+
index 1e7b0d343bab6f4a83de5fbd076ec68f4e500422..0e95cf1d41868e08cb4a93d8d8e42e9453d10d37 100644 (file)
@@ -469,6 +469,23 @@ Scene *BKE_scene_add(Main *bmain, const char *name)
        sce->r.bake_normal_space = R_BAKE_SPACE_TANGENT;
        sce->r.bake_samples = 256;
        sce->r.bake_biasdist = 0.001;
+
+       sce->r.bake.flag = R_BAKE_CLEAR;
+       sce->r.bake.width = 512;
+       sce->r.bake.height = 512;
+       sce->r.bake.margin = 16;
+       sce->r.bake.normal_space = R_BAKE_SPACE_TANGENT;
+       sce->r.bake.normal_swizzle[0] = R_BAKE_POSX;
+       sce->r.bake.normal_swizzle[1] = R_BAKE_POSY;
+       sce->r.bake.normal_swizzle[2] = R_BAKE_POSZ;
+       BLI_strncpy(sce->r.bake.filepath, U.renderdir, sizeof(sce->r.bake.filepath));
+
+       sce->r.bake.im_format.planes = R_IMF_PLANES_RGBA;
+       sce->r.bake.im_format.imtype = R_IMF_IMTYPE_PNG;
+       sce->r.bake.im_format.depth = R_IMF_CHAN_DEPTH_8;
+       sce->r.bake.im_format.quality = 90;
+       sce->r.bake.im_format.compress = 15;
+
        sce->r.scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION;
        sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME;
        sce->r.stamp_font_id = 12;
index 0fe105346a3290dc7d4b122d51097443d3d7a3ec..a6cd854d8744922dc29b55945a5613060ce2f0ad 100644 (file)
@@ -51,6 +51,9 @@
 #include "BKE_main.h"
 #include "BKE_node.h"
 
+#include "BLI_math.h"
+#include "BLI_string.h"
+
 #include "BLO_readfile.h"
 
 #include "readfile.h"
@@ -254,4 +257,26 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
                for (ma = main->mat.first; ma; ma = ma->id.next)
                        ma->mode2 = MA_CASTSHADOW;
        }
+
+       if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "BakeData", "bake")) {
+               Scene *sce;
+
+               for (sce = main->scene.first; sce; sce = sce->id.next) {
+                       sce->r.bake.flag = R_BAKE_CLEAR;
+                       sce->r.bake.width = 512;
+                       sce->r.bake.height = 512;
+                       sce->r.bake.margin = 16;
+                       sce->r.bake.normal_space = R_BAKE_SPACE_TANGENT;
+                       sce->r.bake.normal_swizzle[0] = R_BAKE_POSX;
+                       sce->r.bake.normal_swizzle[1] = R_BAKE_POSY;
+                       sce->r.bake.normal_swizzle[2] = R_BAKE_POSZ;
+                       BLI_strncpy(sce->r.bake.filepath, U.renderdir, sizeof(sce->r.bake.filepath));
+
+                       sce->r.bake.im_format.planes = R_IMF_PLANES_RGBA;
+                       sce->r.bake.im_format.imtype = R_IMF_IMTYPE_PNG;
+                       sce->r.bake.im_format.depth = R_IMF_CHAN_DEPTH_8;
+                       sce->r.bake.im_format.quality = 90;
+                       sce->r.bake.im_format.compress = 15;
+               }
+       }
 }
index 8277c7f7b86ebbc97647c7e3889a2890bc77520c..1bb35b65918163b96df941b46fed634e7629f2f6 100644 (file)
@@ -42,6 +42,7 @@ set(INC_SYS
 set(SRC
        object_add.c
        object_bake.c
+       object_bake_api.c
        object_constraint.c
        object_edit.c
        object_group.c
index 2699d28a10a26f22b878a675a1f22e32f4ab6ee2..94574e81b81a31939e7c31e60a32370e012e0965 100644 (file)
@@ -79,6 +79,7 @@
 #include "WM_types.h"
 
 #include "ED_object.h"
+#include "ED_screen.h"
 
 #include "object_intern.h"
 
@@ -893,4 +894,5 @@ void OBJECT_OT_bake_image(wmOperatorType *ot)
        ot->exec = bake_image_exec;
        ot->invoke = objects_bake_render_invoke;
        ot->modal = objects_bake_render_modal;
+       ot->poll = ED_operator_object_active;
 }
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
new file mode 100644 (file)
index 0000000..813e28b
--- /dev/null
@@ -0,0 +1,1082 @@
+/*
+ * ***** 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) 2004 by Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s):
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/object/object_bake_api.c
+ *  \ingroup edobj
+ */
+
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_material_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_fileops.h"
+#include "BLI_math_geom.h"
+#include "BLI_path_util.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_modifier.h"
+#include "BKE_mesh.h"
+
+#include "RE_engine.h"
+#include "RE_pipeline.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+#include "IMB_colormanagement.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_uvedit.h"
+
+
+#include "object_intern.h"
+
+/* catch esc */
+static int bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+       /* no running blender, remove handler and pass through */
+       if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_RENDER_BAKE))
+               return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+
+       /* running render */
+       switch (event->type) {
+               case ESCKEY:
+               {
+                       G.is_break = true;
+                       return OPERATOR_RUNNING_MODAL;
+               }
+       }
+       return OPERATOR_PASS_THROUGH;
+}
+
+/* for exec() when there is no render job
+ * note: this wont check for the escape key being pressed, but doing so isnt threadsafe */
+static int bake_break(void *UNUSED(rjv))
+{
+       if (G.is_break)
+               return 1;
+       return 0;
+}
+
+static bool write_internal_bake_pixels(
+        Image *image, BakePixel pixel_array[], float *buffer,
+        const int width, const int height, const int margin,
+        const bool is_clear, const bool is_noncolor)
+{
+       ImBuf *ibuf;
+       void *lock;
+       bool is_float;
+       char *mask_buffer = NULL;
+       const int num_pixels = width * height;
+
+       ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
+
+       if (!ibuf)
+               return false;
+
+       if (margin > 0 || !is_clear) {
+               mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask");
+               RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer);
+       }
+
+       is_float = (ibuf->flags & IB_rectfloat);
+
+       /* colormanagement conversions */
+       if (!is_noncolor) {
+               const char *from_colorspace;
+               const char *to_colorspace;
+
+               from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
+
+               if (is_float)
+                       to_colorspace = IMB_colormanagement_get_float_colorspace(ibuf);
+               else
+                       to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf);
+
+               if (from_colorspace != to_colorspace)
+                       IMB_colormanagement_transform(buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false);
+       }
+
+       /* populates the ImBuf */
+       if (is_clear) {
+               if (is_float) {
+                       IMB_buffer_float_from_float(
+                               ibuf->rect_float, buffer, ibuf->channels,
+                               IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false,
+                               ibuf->x, ibuf->y, ibuf->x, ibuf->y);
+               }
+               else {
+                       IMB_buffer_byte_from_float(
+                               (unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither,
+                               IB_PROFILE_SRGB, IB_PROFILE_SRGB,
+                               false, ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+               }
+       }
+       else {
+               if (is_float) {
+                       IMB_buffer_float_from_float_mask(
+                               ibuf->rect_float, buffer, ibuf->channels,
+                               ibuf->x, ibuf->y, ibuf->x, ibuf->y, mask_buffer);
+               }
+               else {
+                       IMB_buffer_byte_from_float_mask(
+                               (unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither,
+                               false, ibuf->x, ibuf->y, ibuf->x, ibuf->x, mask_buffer);
+               }
+       }
+
+       /* margins */
+       if (margin > 0)
+               RE_bake_margin(ibuf, mask_buffer, margin);
+
+       ibuf->userflags |= IB_BITMAPDIRTY;
+       BKE_image_release_ibuf(image, ibuf, NULL);
+
+       if (mask_buffer)
+               MEM_freeN(mask_buffer);
+
+       return true;
+}
+
+static bool write_external_bake_pixels(
+        const char *filepath, BakePixel pixel_array[], float *buffer,
+        const int width, const int height, const int margin,
+        ImageFormatData *im_format, const bool is_noncolor)
+{
+       ImBuf *ibuf = NULL;
+       bool ok = false;
+       bool is_float;
+
+       is_float = im_format->depth > 8;
+
+       /* create a new ImBuf */
+       ibuf = IMB_allocImBuf(width, height, im_format->planes, (is_float ? IB_rectfloat : IB_rect));
+
+       if (!ibuf)
+               return false;
+
+       /* populates the ImBuf */
+       if (is_float) {
+               IMB_buffer_float_from_float(
+                       ibuf->rect_float, buffer, ibuf->channels,
+                       IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false,
+                       ibuf->x, ibuf->y, ibuf->x, ibuf->y);
+       }
+       else {
+               if (!is_noncolor) {
+                       const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
+                       const char *to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf);
+                       IMB_colormanagement_transform(buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false);
+               }
+
+               IMB_buffer_byte_from_float(
+                       (unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither,
+                       IB_PROFILE_SRGB, IB_PROFILE_SRGB,
+                       false, ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+       }
+
+       /* margins */
+       if (margin > 0) {
+               char *mask_buffer = NULL;
+               const int num_pixels = width * height;
+
+               mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask");
+               RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer);
+               RE_bake_margin(ibuf, mask_buffer, margin);
+
+               if (mask_buffer)
+                       MEM_freeN(mask_buffer);
+       }
+
+       if ((ok = BKE_imbuf_write(ibuf, filepath, im_format))) {
+#ifndef WIN32
+               chmod(filepath, S_IRUSR | S_IWUSR);
+#endif
+               //printf("%s saving bake map: '%s'\n", __func__, filepath);
+       }
+
+       /* garbage collection */
+       IMB_freeImBuf(ibuf);
+
+       return ok;
+}
+
+static bool is_noncolor_pass(ScenePassType pass_type)
+{
+       return ELEM7(pass_type,
+                    SCE_PASS_Z,
+                    SCE_PASS_NORMAL,
+                    SCE_PASS_VECTOR,
+                    SCE_PASS_INDEXOB,
+                    SCE_PASS_UV,
+                    SCE_PASS_RAYHITS,
+                    SCE_PASS_INDEXMA);
+}
+
+static bool build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images, ReportList *reports)
+{
+       const int tot_mat = ob->totcol;
+       int i, j;
+       int tot_images = 0;
+
+       /* error handling and tag (in case multiple materials share the same image) */
+       BKE_main_id_tag_idcode(bmain, ID_IM, false);
+
+       for (i = 0; i < tot_mat; i++) {
+               Image *image;
+               ED_object_get_active_image(ob, i + 1, &image, NULL, NULL);
+
+               if (!image) {
+                       if (ob->mat[i]) {
+                               BKE_reportf(reports, RPT_ERROR,
+                                           "No active image found in material %d (%s)", i, ob->mat[i]->id.name + 2);
+                       }
+                       else if (((Mesh *) ob->data)->mat[i]) {
+                               BKE_reportf(reports, RPT_ERROR,
+                                           "No active image found in material %d (%s)", i, ((Mesh *) ob->data)->mat[i]->id.name + 2);
+                       }
+                       else {
+                               BKE_reportf(reports, RPT_ERROR,
+                                           "No active image found in material %d", i);
+                       }
+                       return false;
+               }
+
+               if ((image->id.flag & LIB_DOIT)) {
+                       for (j = 0; j < i; j++) {
+                               if (bake_images->data[j].image == image) {
+                                       bake_images->lookup[i] = j;
+                                       break;
+                               }
+                       }
+               }
+               else {
+                       bake_images->lookup[i] = tot_images;
+                       bake_images->data[tot_images].image = image;
+                       image->id.flag |= LIB_DOIT;
+                       tot_images++;
+               }
+       }
+
+       bake_images->size = tot_images;
+       return true;
+}
+
+/*
+ * returns the total number of pixels
+ */
+static int initialize_internal_images(BakeImages *bake_images, ReportList *reports)
+{
+       int i;
+       int tot_size = 0;
+
+       for (i = 0; i < bake_images->size; i++) {
+               ImBuf *ibuf;
+               void *lock;
+
+               BakeImage *bk_image = &bake_images->data[i];
+               ibuf = BKE_image_acquire_ibuf(bk_image->image, NULL, &lock);
+
+               if (ibuf) {
+                       bk_image->width = ibuf->x;
+                       bk_image->height = ibuf->y;
+                       bk_image->offset = tot_size;
+
+                       tot_size += ibuf->x * ibuf->y;
+               }
+               else {
+                       BKE_image_release_ibuf(bk_image->image, ibuf, lock);
+                       BKE_reportf(reports, RPT_ERROR, "Not initialized image %s", bk_image->image->id.name + 2);
+                       return 0;
+               }
+               BKE_image_release_ibuf(bk_image->image, ibuf, lock);
+       }
+       return tot_size;
+}
+
+typedef struct BakeAPIRender {
+       Object *ob;
+       Main *main;
+       Scene *scene;
+       ReportList *reports;
+       ListBase selected_objects;
+
+       ScenePassType pass_type;
+       int margin;
+
+       int save_mode;
+
+       bool is_clear;
+       bool is_split_materials;
+       bool is_automatic_name;
+       bool use_selected_to_active;
+
+       float cage_extrusion;
+       int normal_space;
+       BakeNormalSwizzle normal_swizzle[3];
+
+       char custom_cage[MAX_NAME];
+       char filepath[FILE_MAX];
+
+       int width;
+       int height;
+       const char *identifier;
+
+       int result;
+       bool ready;
+} BakeAPIRender;
+
+static int bake(Main *bmain, Scene *scene, Object *ob_low, ListBase *selected_objects, ReportList *reports,
+                               const ScenePassType pass_type, const int margin,
+                               const BakeSaveMode save_mode, const bool is_clear, const bool is_split_materials,
+                               const bool is_automatic_name, const bool use_selected_to_active,
+                               const float cage_extrusion, const int normal_space, const BakeNormalSwizzle normal_swizzle[],
+                               const char *custom_cage, const char *filepath, const int width, const int height,
+                               const char *identifier)
+{
+       int op_result = OPERATOR_CANCELLED;
+       bool ok = false;
+
+       Object *ob_cage = NULL;
+
+       BakeHighPolyData *highpoly;
+       int tot_highpoly;
+
+       char restrict_flag_low = ob_low->restrictflag;
+       char restrict_flag_cage;
+
+       Mesh *me_low = NULL;
+       Render *re;
+
+       float *result = NULL;
+
+       BakePixel *pixel_array_low = NULL;
+
+       const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL);
+       const bool is_noncolor = is_noncolor_pass(pass_type);
+       const int depth = RE_pass_depth(pass_type);
+
+       bool is_highpoly = false;
+       bool is_tangent;
+
+       BakeImages bake_images;
+
+       int num_pixels;
+       int tot_materials;
+       int i;
+
+       re = RE_NewRender(scene->id.name);
+
+       is_tangent = pass_type == SCE_PASS_NORMAL && normal_space == R_BAKE_SPACE_TANGENT;
+       tot_materials = ob_low->totcol;
+
+       if (tot_materials == 0) {
+               if (is_save_internal) {
+                       BKE_report(reports, RPT_ERROR,
+                                  "No active image found. Add a material or bake to an external file");
+
+                       goto cleanup;
+               }
+               else if (is_split_materials) {
+                       BKE_report(reports, RPT_ERROR,
+                                  "No active image found. Add a material or bake without the Split Materials option");
+
+                       goto cleanup;
+               }
+               else {
+                       /* baking externally without splitting materials */
+                       tot_materials = 1;
+               }
+       }
+
+       /* we overallocate in case there is more materials than images */
+       bake_images.data = MEM_callocN(sizeof(BakeImage) * tot_materials, "bake images dimensions (width, height, offset)");
+       bake_images.lookup = MEM_callocN(sizeof(int) * tot_materials, "bake images lookup (from material to BakeImage)");
+
+       if (!build_image_lookup(bmain, ob_low, &bake_images, reports))
+               goto cleanup;
+
+       if (is_save_internal) {
+               num_pixels = initialize_internal_images(&bake_images, reports);
+
+               if (num_pixels == 0) {
+                       goto cleanup;
+               }
+
+               if (is_clear) {
+                       RE_bake_ibuf_clear(&bake_images, is_tangent);
+               }
+       }
+       else {
+               /* when saving extenally always use the size specified in the UI */
+
+               num_pixels = width * height * bake_images.size;
+
+               for (i = 0; i < bake_images.size; i++) {
+                       bake_images.data[i].width = width;
+                       bake_images.data[i].height = height;
+                       bake_images.data[i].offset = (is_split_materials ? num_pixels : 0);
+                       bake_images.data[i].image = NULL;
+               }
+
+               if (!is_split_materials) {
+                       /* saving a single image */
+                       for (i = 0; i < tot_materials; i++)
+                               bake_images.lookup[i] = 0;
+               }
+       }
+
+       if (use_selected_to_active) {
+               CollectionPointerLink *link;
+               tot_highpoly = 0;
+
+               for (link = selected_objects->first; link; link = link->next) {
+                       Object *ob_iter = link->ptr.data;
+
+                       if (ob_iter == ob_low)
+                               continue;
+
+                       tot_highpoly ++;
+               }
+
+               if (tot_highpoly == 0) {
+                       BKE_report(reports, RPT_ERROR, "No valid selected objects");
+                       op_result = OPERATOR_CANCELLED;
+
+                       goto cleanup;
+               }
+               else {
+                       is_highpoly = true;
+               }
+       }
+
+       if (custom_cage[0] != '\0') {
+               ob_cage = BLI_findstring(&bmain->object, custom_cage, offsetof(ID, name) + 2);
+
+               /* TODO check if cage object has the same topology (num of triangles and a valid UV) */
+               if (ob_cage == NULL || ob_cage->type != OB_MESH) {
+                       BKE_report(reports, RPT_ERROR, "No valid cage object");
+                       op_result = OPERATOR_CANCELLED;
+
+                       goto cleanup;
+               }
+               else {
+                       restrict_flag_cage = ob_cage->restrictflag;
+               }
+       }
+
+       RE_bake_engine_set_engine_parameters(re, bmain, scene);
+
+       /* blender_test_break uses this global */
+       G.is_break = false;
+
+       RE_test_break_cb(re, NULL, bake_break);
+
+       pixel_array_low = MEM_callocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly");
+       result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels");
+
+       if (is_highpoly) {
+               CollectionPointerLink *link;
+               ModifierData *md, *nmd;
+               ListBase modifiers_tmp, modifiers_original;
+               float mat_low[4][4];
+               int i = 0;
+               highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects");
+
+               /* prepare cage mesh */
+               if (ob_cage) {
+                       me_low = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0);
+                       copy_m4_m4(mat_low, ob_cage->obmat);
+               }
+               else {
+                       modifiers_original = ob_low->modifiers;
+                       BLI_listbase_clear(&modifiers_tmp);
+
+                       for (md = ob_low->modifiers.first; md; md = md->next) {
+                               /* Edge Split cannot be applied in the cage,
+                                * the cage is supposed to have interpolated normals
+                                * between the faces unless the geometry is physically
+                                * split. So we create a copy of the low poly mesh without
+                                * the eventual edge split.*/
+
+                               if (md->type == eModifierType_EdgeSplit)
+                                       continue;
+
+                               nmd = modifier_new(md->type);
+                               BLI_strncpy(nmd->name, md->name, sizeof(nmd->name));
+                               modifier_copyData(md, nmd);
+                               BLI_addtail(&modifiers_tmp, nmd);
+                       }
+
+                       /* temporarily replace the modifiers */
+                       ob_low->modifiers = modifiers_tmp;
+
+                       /* get the cage mesh as it arrives in the renderer */
+                       me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
+                       copy_m4_m4(mat_low, ob_low->obmat);
+               }
+
+               /* populate highpoly array */
+               for (link = selected_objects->first; link; link = link->next) {
+                       TriangulateModifierData *tmd;
+                       Object *ob_iter = link->ptr.data;
+
+                       if (ob_iter == ob_low)
+                               continue;
+
+                       /* initialize highpoly_data */
+                       highpoly[i].ob = ob_iter;
+                       highpoly[i].me = NULL;
+                       highpoly[i].tri_mod = NULL;
+                       highpoly[i].restrict_flag = ob_iter->restrictflag;
+                       highpoly[i].pixel_array = MEM_callocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly");
+
+
+                       /* triangulating so BVH returns the primitive_id that will be used for rendering */
+                       highpoly[i].tri_mod = ED_object_modifier_add(
+                               reports, bmain, scene, highpoly[i].ob,
+                               "TmpTriangulate", eModifierType_Triangulate);
+                       tmd = (TriangulateModifierData *)highpoly[i].tri_mod;
+                       tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED;
+                       tmd->ngon_method = MOD_TRIANGULATE_NGON_EARCLIP;
+
+                       highpoly[i].me = BKE_mesh_new_from_object(bmain, scene, highpoly[i].ob, 1, 2, 1, 0);
+                       highpoly[i].ob->restrictflag &= ~OB_RESTRICT_RENDER;
+
+                       /* lowpoly to highpoly transformation matrix */
+                       invert_m4_m4(highpoly[i].mat_lowtohigh, highpoly[i].ob->obmat);
+                       mul_m4_m4m4(highpoly[i].mat_lowtohigh, highpoly[i].mat_lowtohigh, mat_low);
+
+                       i++;
+               }
+
+               BLI_assert(i == tot_highpoly);
+
+               /* populate the pixel array with the face data */
+               RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images);
+
+               ob_low->restrictflag |= OB_RESTRICT_RENDER;
+
+               /* populate the pixel arrays with the corresponding face data for each high poly object */
+               RE_bake_pixels_populate_from_objects(
+                       me_low, pixel_array_low, highpoly, tot_highpoly,
+                       num_pixels, cage_extrusion);
+
+               /* the baking itself */
+               for (i = 0; i < tot_highpoly; i++) {
+                       if (RE_bake_has_engine(re)) {
+                               ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels,
+                                                   depth, pass_type, result);
+                       }
+                       else {
+                               ok = RE_bake_internal(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels,
+                                                     depth, pass_type, result);
+                       }
+
+                       if (!ok)
+                               break;
+               }
+
+               /* reverting data back */
+               if (ob_cage) {
+                       ob_cage->restrictflag |= OB_RESTRICT_RENDER;
+               }
+               else {
+                       ob_low->modifiers = modifiers_original;
+
+                       while ((md = BLI_pophead(&modifiers_tmp))) {
+                               modifier_free(md);
+                       }
+               }
+       }
+       else {
+               /* get the mesh as it arrives in the renderer */
+               me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
+
+               /* populate the pixel array with the face data */
+               RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images);
+
+               /* make sure low poly renders */
+               ob_low->restrictflag &= ~OB_RESTRICT_RENDER;
+
+               if (RE_bake_has_engine(re))
+                       ok = RE_bake_engine(re, ob_low, pixel_array_low, num_pixels, depth, pass_type, result);
+               else
+                       ok = RE_bake_internal(re, ob_low, pixel_array_low, num_pixels, depth, pass_type, result);
+       }
+
+       /* normal space conversion
+        * the normals are expected to be in world space, +X +Y +Z */
+       if (pass_type == SCE_PASS_NORMAL) {
+               switch (normal_space) {
+                       case R_BAKE_SPACE_WORLD:
+                       {
+                               /* Cycles internal format */
+                               if ((normal_swizzle[0] == R_BAKE_POSX) &&
+                                   (normal_swizzle[1] == R_BAKE_POSY) &&
+                                   (normal_swizzle[2] == R_BAKE_POSZ))
+                               {
+                                       break;
+                               }
+                               else {
+                                       RE_bake_normal_world_to_world(pixel_array_low, num_pixels,  depth, result, normal_swizzle);
+                               }
+                               break;
+                       }
+                       case R_BAKE_SPACE_OBJECT:
+                       {
+                               RE_bake_normal_world_to_object(pixel_array_low, num_pixels, depth, result, ob_low, normal_swizzle);
+                               break;
+                       }
+                       case R_BAKE_SPACE_TANGENT:
+                       {
+                               if (is_highpoly) {
+                                       RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_low, normal_swizzle);
+                               }
+                               else {
+                                       /* from multiresolution */
+                                       Mesh *me_nores = NULL;
+                                       ModifierData *md = NULL;
+                                       int mode;
+
+                                       md = modifiers_findByType(ob_low, eModifierType_Multires);
+
+                                       if (md) {
+                                               mode = md->mode;
+                                               md->mode &= ~eModifierMode_Render;
+                                       }
+
+                                       me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0);
+                                       RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images);
+
+                                       RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle);
+                                       BKE_libblock_free(bmain, me_nores);
+
+                                       if (md)
+                                               md->mode = mode;
+                               }
+                               break;
+                       }
+                       default:
+                               break;
+               }
+       }
+
+       if (!ok) {
+               BKE_report(reports, RPT_ERROR, "Problem baking object map");
+               op_result = OPERATOR_CANCELLED;
+       }
+       else {
+               /* save the results */
+               for (i = 0; i < bake_images.size; i++) {
+                       BakeImage *bk_image = &bake_images.data[i];
+
+                       if (is_save_internal) {
+                               ok = write_internal_bake_pixels(
+                                        bk_image->image,
+                                        pixel_array_low + bk_image->offset,
+                                        result + bk_image->offset * depth,
+                                        bk_image->width, bk_image->height,
+                                        margin, is_clear, is_noncolor);
+
+                               if (!ok) {
+                                       BKE_report(reports, RPT_ERROR,
+                                                  "Problem saving the bake map internally, "
+                                                  "make sure there is a Texture Image node in the current object material");
+                                       op_result = OPERATOR_CANCELLED;
+                               }
+                               else {
+                                       BKE_report(reports, RPT_INFO,
+                                                  "Baking map saved to internal image, save it externally or pack it");
+                                       op_result = OPERATOR_FINISHED;
+                               }
+                       }
+                       /* save externally */
+                       else {
+                               BakeData *bake = &scene->r.bake;
+                               char name[FILE_MAX];
+
+                               BKE_makepicstring_from_type(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false);
+
+                               if (is_automatic_name) {
+                                       BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_");
+                                       BLI_path_suffix(name, FILE_MAX, identifier, "_");
+                               }
+
+                               if (is_split_materials) {
+                                       if (bk_image->image) {
+                                               BLI_path_suffix(name, FILE_MAX, bk_image->image->id.name + 2, "_");
+                                       }
+                                       else {
+                                               if (ob_low->mat[i]) {
+                                                       BLI_path_suffix(name, FILE_MAX, ob_low->mat[i]->id.name + 2, "_");
+                                               }
+                                               else if (me_low->mat[i]) {
+                                                       BLI_path_suffix(name, FILE_MAX, me_low->mat[i]->id.name + 2, "_");
+                                               }
+                                               else {
+                                                       /* if everything else fails, use the material index */
+                                                       char tmp[4];
+                                                       sprintf(tmp, "%d", i % 1000);
+                                                       BLI_path_suffix(name, FILE_MAX, tmp, "_");
+                                               }
+                                       }
+                               }
+
+                               /* save it externally */
+                               ok = write_external_bake_pixels(
+                                       name,
+                                       pixel_array_low + bk_image->offset,
+                                       result + bk_image->offset * depth,
+                                       bk_image->width, bk_image->height,
+                                       margin, &bake->im_format, is_noncolor);
+
+                               if (!ok) {
+                                       BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\".", name);
+                                       op_result = OPERATOR_CANCELLED;
+                               }
+                               else {
+                                       BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\".", name);
+                                       op_result = OPERATOR_FINISHED;
+                               }
+
+                               if (!is_split_materials) {
+                                       break;
+                               }
+                       }
+               }
+       }
+
+
+cleanup:
+
+       if (is_highpoly) {
+               int i;
+               for (i = 0; i < tot_highpoly; i++) {
+                       highpoly[i].ob->restrictflag = highpoly[i].restrict_flag;
+
+                       if (highpoly[i].pixel_array)
+                               MEM_freeN(highpoly[i].pixel_array);
+
+                       if (highpoly[i].tri_mod)
+                               ED_object_modifier_remove(reports, bmain, highpoly[i].ob, highpoly[i].tri_mod);
+
+                       if (highpoly[i].me)
+                               BKE_libblock_free(bmain, highpoly[i].me);
+               }
+               MEM_freeN(highpoly);
+       }
+
+       ob_low->restrictflag = restrict_flag_low;
+
+       if (ob_cage)
+               ob_cage->restrictflag = restrict_flag_cage;
+
+       if (pixel_array_low)
+               MEM_freeN(pixel_array_low);
+
+       if (bake_images.data)
+               MEM_freeN(bake_images.data);
+
+       if (bake_images.lookup)
+               MEM_freeN(bake_images.lookup);
+
+       if (result)
+               MEM_freeN(result);
+
+       if (me_low)
+               BKE_libblock_free(bmain, me_low);
+
+       RE_SetReports(re, NULL);
+
+       return op_result;
+}
+
+static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr)
+{
+       bool is_save_internal;
+
+       bkr->ob = CTX_data_active_object(C);
+       bkr->main = CTX_data_main(C);
+       bkr->scene = CTX_data_scene(C);
+
+       bkr->pass_type = RNA_enum_get(op->ptr, "type");
+       bkr->margin = RNA_int_get(op->ptr, "margin");
+
+       bkr->save_mode = RNA_enum_get(op->ptr, "save_mode");
+       is_save_internal = (bkr->save_mode == R_BAKE_SAVE_INTERNAL);
+
+       bkr->is_clear = RNA_boolean_get(op->ptr, "use_clear");
+       bkr->is_split_materials = (!is_save_internal) && RNA_boolean_get(op->ptr, "use_split_materials");
+       bkr->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name");
+       bkr->use_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active");
+       bkr->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion");
+
+       bkr->normal_space = RNA_enum_get(op->ptr, "normal_space");
+       bkr->normal_swizzle[0] = RNA_enum_get(op->ptr, "normal_r");
+       bkr->normal_swizzle[1] = RNA_enum_get(op->ptr, "normal_g");
+       bkr->normal_swizzle[2] = RNA_enum_get(op->ptr, "normal_b");
+
+       bkr->width = RNA_int_get(op->ptr, "width");
+       bkr->height = RNA_int_get(op->ptr, "height");
+       bkr->identifier = "";
+
+       RNA_string_get(op->ptr, "cage", bkr->custom_cage);
+
+       if ((!is_save_internal) && bkr->is_automatic_name) {
+               PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
+               RNA_property_enum_identifier(C, op->ptr, prop, bkr->pass_type, &bkr->identifier);
+       }
+
+       if (bkr->use_selected_to_active)
+               CTX_data_selected_objects(C, &bkr->selected_objects);
+
+       bkr->reports = op->reports;
+
+       /* XXX hack to force saving to always be internal. Whether (and how) to support
+        * external saving will be addressed later */
+       bkr->save_mode = R_BAKE_SAVE_INTERNAL;
+}
+
+static int bake_exec(bContext *C, wmOperator *op)
+{
+       int result;
+       BakeAPIRender bkr = {NULL};
+
+       bake_init_api_data(op, C, &bkr);
+
+       result = bake(bkr.main, bkr.scene, bkr.ob, &bkr.selected_objects, bkr.reports,
+                             bkr.pass_type, bkr.margin, bkr.save_mode,
+                             bkr.is_clear, bkr.is_split_materials, bkr.is_automatic_name, bkr.use_selected_to_active,
+                             bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle,
+                                 bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier);
+
+       BLI_freelistN(&bkr.selected_objects);
+       return result;
+}
+
+static void bake_startjob(void *bkv, short *UNUSED(stop), short *UNUSED(do_update), float *UNUSED(progress))
+{
+       BakeAPIRender *bkr = (BakeAPIRender *)bkv;
+
+       bkr->result = bake(bkr->main, bkr->scene, bkr->ob, &bkr->selected_objects, bkr->reports,
+                                          bkr->pass_type, bkr->margin, bkr->save_mode,
+                                          bkr->is_clear, bkr->is_split_materials, bkr->is_automatic_name, bkr->use_selected_to_active,
+                                          bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle,
+                                          bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier
+                                          );
+}
+
+static void bake_freejob(void *bkv)
+{
+       BakeAPIRender *bkr = (BakeAPIRender *)bkv;
+
+       BLI_freelistN(&bkr->selected_objects);
+       MEM_freeN(bkr);
+
+       G.is_rendering = false;
+}
+
+static void bake_set_props(wmOperator *op, Scene *scene)
+{
+       PropertyRNA *prop;
+       BakeData *bake = &scene->r.bake;
+
+       prop = RNA_struct_find_property(op->ptr, "filepath");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_string_set(op->ptr, prop, bake->filepath);
+       }
+
+       prop =  RNA_struct_find_property(op->ptr, "width");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_int_set(op->ptr, prop, bake->width);
+       }
+
+       prop =  RNA_struct_find_property(op->ptr, "height");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_int_set(op->ptr, prop, bake->width);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "margin");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_int_set(op->ptr, prop, bake->margin);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "use_selected_to_active");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_TO_ACTIVE));
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "cage_extrusion");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_float_set(op->ptr, prop, bake->cage_extrusion);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "cage");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_string_set(op->ptr, prop, bake->cage);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "normal_space");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_enum_set(op->ptr, prop, bake->normal_space);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "normal_r");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[0]);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "normal_g");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[1]);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "normal_b");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[2]);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "save_mode");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_enum_set(op->ptr, prop, bake->save_mode);
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "use_clear");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CLEAR));
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "use_split_materials");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_SPLIT_MAT));
+       }
+
+       prop = RNA_struct_find_property(op->ptr, "use_automatic_name");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_AUTO_NAME));
+       }
+}
+
+static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+       wmJob *wm_job;
+       BakeAPIRender *bkr;
+       Scene *scene = CTX_data_scene(C);
+
+       bake_set_props(op, scene);
+
+       /* only one render job at a time */
+       if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE))
+               return OPERATOR_CANCELLED;
+
+       bkr = MEM_callocN(sizeof(BakeAPIRender), "render bake");
+
+       /* init bake render */
+       bake_init_api_data(op, C, bkr);
+
+       /* setup job */
+       wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Texture Bake",
+                                                WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE);
+       WM_jobs_customdata_set(wm_job, bkr, bake_freejob);
+       WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */
+       WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL);
+
+       G.is_break = false;
+       G.is_rendering = true;
+
+       WM_jobs_start(CTX_wm_manager(C), wm_job);
+
+       WM_cursor_wait(0);
+
+       /* add modal handler for ESC */
+       WM_event_add_modal_handler(C, op);
+
+       WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene);
+       return OPERATOR_RUNNING_MODAL;
+}
+
+void OBJECT_OT_bake(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Bake";
+       ot->description = "Bake image textures of selected objects";
+       ot->idname = "OBJECT_OT_bake";
+
+       /* api callbacks */
+       ot->exec = bake_exec;
+       ot->modal = bake_modal;
+       ot->invoke = bake_invoke;
+       ot->poll = ED_operator_object_active_editable_mesh;
+
+       RNA_def_enum(ot->srna, "type", render_pass_type_items, SCE_PASS_COMBINED, "Type",
+                    "Type of pass to bake, some of them may not be supported by the current render engine");
+       RNA_def_string_file_path(ot->srna, "filepath", NULL, FILE_MAX, "File Path",
+                                "Image filepath to use when saving externally");
+       RNA_def_int(ot->srna, "width", 512, 1, INT_MAX, "Width",
+                   "Horizontal dimension of the baking map (external only)", 64, 4096);
+       RNA_def_int(ot->srna, "height", 512, 1, INT_MAX, "Height",
+                   "Vertical dimension of the baking map (external only)", 64, 4096);
+       RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin",
+                   "Extends the baked result as a post process filter", 0, 64);
+       RNA_def_boolean(ot->srna, "use_selected_to_active", false, "Selected to Active",
+                       "Bake shading on the surface of selected objects to the active object");
+       RNA_def_float(ot->srna, "cage_extrusion", 0.0, 0.0, 1.0, "Cage Extrusion",
+                     "Distance to use for the inward ray cast when using selected to active", 0.0, 1.0);
+       RNA_def_string(ot->srna, "cage", NULL, MAX_NAME, "Cage",
+                      "Object to use as cage");
+       RNA_def_enum(ot->srna, "normal_space", normal_space_items, R_BAKE_SPACE_TANGENT, "Normal Space",
+                    "Choose normal space for baking");
+       RNA_def_enum(ot->srna, "normal_r", normal_swizzle_items, R_BAKE_POSX, "R", "Axis to bake in red channel");
+       RNA_def_enum(ot->srna, "normal_g", normal_swizzle_items, R_BAKE_POSY, "G", "Axis to bake in green channel");
+       RNA_def_enum(ot->srna, "normal_b", normal_swizzle_items, R_BAKE_POSZ, "B", "Axis to bake in blue channel");
+       RNA_def_enum(ot->srna, "save_mode", bake_save_mode_items, R_BAKE_SAVE_INTERNAL, "Save Mode",
+                    "Choose how to save the baking map");
+       RNA_def_boolean(ot->srna, "use_clear", false, "Clear",
+                       "Clear Images before baking (only for internal saving)");
+       RNA_def_boolean(ot->srna, "use_split_materials", false, "Split Materials",
+                       "Split baked maps per material, using material name in output file (external only)");
+       RNA_def_boolean(ot->srna, "use_automatic_name", false, "Automatic Name",
+                       "Automatically name the output file with the pass type");
+}
index 5864f1e618e3df033c74c80f3c7a219facebcc26..fd6b9a1bad0a75d5885eab65a2b1cadc94d7183f 100644 (file)
@@ -254,6 +254,7 @@ void OBJECT_OT_group_remove(struct wmOperatorType *ot);
 
 /* object_bake.c */
 void OBJECT_OT_bake_image(wmOperatorType *ot);
+void OBJECT_OT_bake(wmOperatorType *ot);
 
 /* object_lod.c */
 void OBJECT_OT_lod_add(struct wmOperatorType *ot);
index 119107a07043f8cc9233134007b5c8509accb133..a8f07747d3a740a8d01cde68a3b914c0314199f1 100644 (file)
@@ -239,6 +239,7 @@ void ED_operatortypes_object(void)
        WM_operatortype_append(OBJECT_OT_hook_recenter);
 
        WM_operatortype_append(OBJECT_OT_bake_image);
+       WM_operatortype_append(OBJECT_OT_bake);
        WM_operatortype_append(OBJECT_OT_drop_named_material);
        WM_operatortype_append(OBJECT_OT_laplaciandeform_bind);
 
index 5a47e76b4f948d3ba2187199f24e78a8f784bac7..ceb938c3af29cbcad3bf14f22d014c2f73ee331d 100644 (file)
@@ -210,37 +210,39 @@ typedef struct SceneRenderLayer {
 #define SCE_LAY_NEG_ZMASK      0x80000
 
 /* srl->passflag */
-#define SCE_PASS_COMBINED                              (1<<0)
-#define SCE_PASS_Z                                             (1<<1)
-#define SCE_PASS_RGBA                                  (1<<2)
-#define SCE_PASS_DIFFUSE                               (1<<3)
-#define SCE_PASS_SPEC                                  (1<<4)
-#define SCE_PASS_SHADOW                                        (1<<5)
-#define SCE_PASS_AO                                            (1<<6)
-#define SCE_PASS_REFLECT                               (1<<7)
-#define SCE_PASS_NORMAL                                        (1<<8)
-#define SCE_PASS_VECTOR                                        (1<<9)
-#define SCE_PASS_REFRACT                               (1<<10)
-#define SCE_PASS_INDEXOB                               (1<<11)
-#define SCE_PASS_UV                                            (1<<12)
-#define SCE_PASS_INDIRECT                              (1<<13)
-#define SCE_PASS_MIST                                  (1<<14)
-#define SCE_PASS_RAYHITS                               (1<<15)
-#define SCE_PASS_EMIT                                  (1<<16)
-#define SCE_PASS_ENVIRONMENT                   (1<<17)
-#define SCE_PASS_INDEXMA                               (1<<18)
-#define SCE_PASS_DIFFUSE_DIRECT                        (1<<19)
-#define SCE_PASS_DIFFUSE_INDIRECT              (1<<20)
-#define SCE_PASS_DIFFUSE_COLOR                 (1<<21)
-#define SCE_PASS_GLOSSY_DIRECT                 (1<<22)
-#define SCE_PASS_GLOSSY_INDIRECT               (1<<23)
-#define SCE_PASS_GLOSSY_COLOR                  (1<<24)
-#define SCE_PASS_TRANSM_DIRECT                 (1<<25)
-#define SCE_PASS_TRANSM_INDIRECT               (1<<26)
-#define SCE_PASS_TRANSM_COLOR                  (1<<27)
-#define SCE_PASS_SUBSURFACE_DIRECT             (1<<28)
-#define SCE_PASS_SUBSURFACE_INDIRECT   (1<<29)
-#define SCE_PASS_SUBSURFACE_COLOR              (1<<30)
+typedef enum ScenePassType {
+       SCE_PASS_COMBINED                 = (1 << 0),
+       SCE_PASS_Z                        = (1 << 1),
+       SCE_PASS_RGBA                     = (1 << 2),
+       SCE_PASS_DIFFUSE                  = (1 << 3),
+       SCE_PASS_SPEC                     = (1 << 4),
+       SCE_PASS_SHADOW                   = (1 << 5),
+       SCE_PASS_AO                       = (1 << 6),
+       SCE_PASS_REFLECT                  = (1 << 7),
+       SCE_PASS_NORMAL                   = (1 << 8),
+       SCE_PASS_VECTOR                   = (1 << 9),
+       SCE_PASS_REFRACT                  = (1 << 10),
+       SCE_PASS_INDEXOB                  = (1 << 11),
+       SCE_PASS_UV                       = (1 << 12),
+       SCE_PASS_INDIRECT                 = (1 << 13),
+       SCE_PASS_MIST                     = (1 << 14),
+       SCE_PASS_RAYHITS                  = (1 << 15),
+       SCE_PASS_EMIT                     = (1 << 16),
+       SCE_PASS_ENVIRONMENT              = (1 << 17),
+       SCE_PASS_INDEXMA                  = (1 << 18),
+       SCE_PASS_DIFFUSE_DIRECT           = (1 << 19),
+       SCE_PASS_DIFFUSE_INDIRECT         = (1 << 20),
+       SCE_PASS_DIFFUSE_COLOR            = (1 << 21),
+       SCE_PASS_GLOSSY_DIRECT            = (1 << 22),
+       SCE_PASS_GLOSSY_INDIRECT          = (1 << 23),
+       SCE_PASS_GLOSSY_COLOR             = (1 << 24),
+       SCE_PASS_TRANSM_DIRECT            = (1 << 25),
+       SCE_PASS_TRANSM_INDIRECT          = (1 << 26),
+       SCE_PASS_TRANSM_COLOR             = (1 << 27),
+       SCE_PASS_SUBSURFACE_DIRECT        = (1 << 28),
+       SCE_PASS_SUBSURFACE_INDIRECT      = (1 << 29),
+       SCE_PASS_SUBSURFACE_COLOR         = (1 << 30),
+} ScenePassType;
 
 /* note, srl->passflag is treestore element 'nr' in outliner, short still... */
 
@@ -358,6 +360,42 @@ typedef struct ImageFormatData {
 /* ImageFormatData.cineon_flag */
 #define R_IMF_CINEON_FLAG_LOG (1<<0)  /* was R_CINEON_LOG */
 
+typedef struct BakeData {
+       struct ImageFormatData im_format;
+
+       char filepath[1024]; /* FILE_MAX */
+
+       short width, height;
+       short margin, flag;
+
+       float cage_extrusion;
+       float pad2;
+
+       char normal_swizzle[3];
+       char normal_space;
+
+       char save_mode;
+       char pad[3];
+
+       char cage[64];  /* MAX_NAME */
+} BakeData;
+
+/* (char) normal_swizzle */
+typedef enum BakeNormalSwizzle {
+       R_BAKE_POSX = 0,
+       R_BAKE_POSY = 1,
+       R_BAKE_POSZ = 2,
+       R_BAKE_NEGX = 3,
+       R_BAKE_NEGY = 4,
+       R_BAKE_NEGZ = 5,
+} BakeNormalSwizzle;
+
+/* (char) save_mode */
+typedef enum BakeSaveMode {
+       R_BAKE_SAVE_INTERNAL = 0,
+       R_BAKE_SAVE_EXTERNAL = 1,
+} BakeSaveMode;
+
 /* *************************************************************** */
 /* Render Data */
 
@@ -563,6 +601,9 @@ typedef struct RenderData {
 
        /* render engine */
        char engine[32];
+
+       /* Cycles baking */
+       struct BakeData bake;
 } RenderData;
 
 /* *************************************************************** */
@@ -1401,6 +1442,8 @@ enum {
 #define R_BAKE_LORES_MESH      32
 #define R_BAKE_VCOL                    64
 #define R_BAKE_USERSCALE       128
+#define R_BAKE_SPLIT_MAT       256
+#define R_BAKE_AUTO_NAME       512
 
 /* bake_normal_space */
 #define R_BAKE_SPACE_CAMERA     0
index 0b73fc932a99c545aa0f54f98fb3c22f8b4f201d..3d4ed0d6f5123cee44ffae3f5fec52fc737e0e47 100644 (file)
@@ -65,9 +65,14 @@ extern EnumPropertyItem modifier_triangulate_ngon_method_items[];
 
 extern EnumPropertyItem image_type_items[];
 extern EnumPropertyItem image_color_mode_items[];
-extern EnumPropertyItem image_depth_mode_items[];
+extern EnumPropertyItem image_color_depth_items[];
 extern EnumPropertyItem image_generated_type_items[];
 
+extern EnumPropertyItem normal_space_items[];
+extern EnumPropertyItem normal_swizzle_items[];
+extern EnumPropertyItem bake_save_mode_items[];
+
+extern EnumPropertyItem exr_codec_items[];
 extern EnumPropertyItem color_sets_items[];
 
 extern EnumPropertyItem beztriple_keyframe_type_items[];
@@ -119,6 +124,8 @@ extern EnumPropertyItem object_axis_unsigned_items[];
 
 extern EnumPropertyItem controller_type_items[];
 
+extern EnumPropertyItem render_pass_type_items[];
+
 extern EnumPropertyItem keymap_propvalue_items[];
 
 extern EnumPropertyItem operator_context_items[];
index 550fe4b0baaad83df5d3dedfabc261065f933fd3..bac1f1321268b5d82e279f4ecb40adb5b37d5a02 100644 (file)
@@ -292,216 +292,19 @@ Mesh *rna_Main_meshes_new_from_object(
         Main *bmain, ReportList *reports, Scene *sce,
         Object *ob, int apply_modifiers, int settings, int calc_tessface, int calc_undeformed)
 {
-       Mesh *tmpmesh;
-       Curve *tmpcu = NULL, *copycu;
-       Object *tmpobj = NULL;
-       const bool use_render_resolution = (settings == eModifierMode_Render);
-       int i;
-       int cage = !apply_modifiers;
-
-       /* perform the mesh extraction based on type */
        switch (ob->type) {
                case OB_FONT:
                case OB_CURVE:
                case OB_SURF:
-               {
-                       ListBase dispbase = {NULL, NULL};
-                       DerivedMesh *derivedFinal = NULL;
-                       int uv_from_orco;
-
-                       /* copies object and modifiers (but not the data) */
-                       tmpobj = BKE_object_copy_ex(bmain, ob, true);
-                       tmpcu = (Curve *)tmpobj->data;
-                       tmpcu->id.us--;
-
-                       /* if getting the original caged mesh, delete object modifiers */
-                       if (cage)
-                               BKE_object_free_modifiers(tmpobj);
-
-                       /* copies the data */
-                       copycu = tmpobj->data = BKE_curve_copy((Curve *) ob->data);
-
-                       /* temporarily set edit so we get updates from edit mode, but
-                        * also because for text datablocks copying it while in edit
-                        * mode gives invalid data structures */
-                       copycu->editfont = tmpcu->editfont;
-                       copycu->editnurb = tmpcu->editnurb;
-
-                       /* get updated display list, and convert to a mesh */
-                       BKE_displist_make_curveTypes_forRender(sce, tmpobj, &dispbase, &derivedFinal, false, use_render_resolution);
-
-                       copycu->editfont = NULL;
-                       copycu->editnurb = NULL;
-
-                       tmpobj->derivedFinal = derivedFinal;
-
-                       /* convert object type to mesh */
-                       uv_from_orco = (tmpcu->flag & CU_UV_ORCO) != 0;
-                       BKE_mesh_from_nurbs_displist(tmpobj, &dispbase, uv_from_orco);
-
-                       tmpmesh = tmpobj->data;
-
-                       BKE_displist_free(&dispbase);
-
-                       /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked.
-                        * if it didn't the curve did not have any segments or otherwise 
-                        * would have generated an empty mesh */
-                       if (tmpobj->type != OB_MESH) {
-                               BKE_libblock_free_us(G.main, tmpobj);
-                               return NULL;
-                       }
-
-                       BKE_mesh_texspace_copy_from_object(tmpmesh, ob);
-
-                       BKE_libblock_free_us(bmain, tmpobj);
-                       break;
-               }
-
                case OB_MBALL:
-               {
-                       /* metaballs don't have modifiers, so just convert to mesh */
-                       Object *basis_ob = BKE_mball_basis_find(sce, ob);
-                       /* todo, re-generatre for render-res */
-                       /* metaball_polygonize(scene, ob) */
-
-                       if (ob != basis_ob)
-                               return NULL;  /* only do basis metaball */
-
-                       tmpmesh = BKE_mesh_add(bmain, "Mesh");
-                       /* BKE_mesh_add gives us a user count we don't need */
-                       tmpmesh->id.us--;
-
-                       if (use_render_resolution) {
-                               ListBase disp = {NULL, NULL};
-                               /* TODO(sergey): This is gonna to work for until EvaluationContext
-                                *               only contains for_render flag. As soon as CoW is
-                                *               implemented, this is to be rethinked.
-                                */
-                               EvaluationContext eval_ctx = {0};
-                               eval_ctx.for_render = use_render_resolution;
-                               BKE_displist_make_mball_forRender(&eval_ctx, sce, ob, &disp);
-                               BKE_mesh_from_metaball(&disp, tmpmesh);
-                               BKE_displist_free(&disp);
-                       }
-                       else {
-                               ListBase disp = {NULL, NULL};
-                               if (ob->curve_cache) {
-                                       disp = ob->curve_cache->disp;
-                               }
-                               BKE_mesh_from_metaball(&disp, tmpmesh);
-                       }
-
-                       BKE_mesh_texspace_copy_from_object(tmpmesh, ob);
-
-                       break;
-
-               }
                case OB_MESH:
-                       /* copies object and modifiers (but not the data) */
-                       if (cage) {
-                               /* copies the data */
-                               tmpmesh = BKE_mesh_copy_ex(bmain, ob->data);
-                               /* if not getting the original caged mesh, get final derived mesh */
-                       }
-                       else {
-                               /* Make a dummy mesh, saves copying */
-                               DerivedMesh *dm;
-                               /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */
-                               CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter,
-                                                                * for example, needs CD_MASK_MDEFORMVERT */
-
-                               if (calc_undeformed)
-                                       mask |= CD_MASK_ORCO;
-
-                               /* Write the display mesh into the dummy mesh */
-                               if (use_render_resolution)
-                                       dm = mesh_create_derived_render(sce, ob, mask);
-                               else
-                                       dm = mesh_create_derived_view(sce, ob, mask);
-
-                               tmpmesh = BKE_mesh_add(bmain, "Mesh");
-                               DM_to_mesh(dm, tmpmesh, ob, mask);
-                               dm->release(dm);
-                       }
-
-                       /* BKE_mesh_add/copy gives us a user count we don't need */
-                       tmpmesh->id.us--;
-
                        break;
                default:
                        BKE_report(reports, RPT_ERROR, "Object does not have geometry data");
                        return NULL;
        }
 
-       /* Copy materials to new mesh */
-       switch (ob->type) {
-               case OB_SURF:
-               case OB_FONT:
-               case OB_CURVE:
-                       tmpmesh->totcol = tmpcu->totcol;
-
-                       /* free old material list (if it exists) and adjust user counts */
-                       if (tmpcu->mat) {
-                               for (i = tmpcu->totcol; i-- > 0; ) {
-                                       /* are we an object material or data based? */
-
-                                       tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : tmpcu->mat[i];
-
-                                       if (tmpmesh->mat[i]) {
-                                               tmpmesh->mat[i]->id.us++;
-                                       }
-                               }
-                       }
-                       break;
-
-#if 0
-               /* Crashes when assigning the new material, not sure why */
-               case OB_MBALL:
-                       tmpmb = (MetaBall *)ob->data;
-                       tmpmesh->totcol = tmpmb->totcol;
-
-                       /* free old material list (if it exists) and adjust user counts */
-                       if (tmpmb->mat) {
-                               for (i = tmpmb->totcol; i-- > 0; ) {
-                                       tmpmesh->mat[i] = tmpmb->mat[i]; /* CRASH HERE ??? */
-                                       if (tmpmesh->mat[i]) {
-                                               tmpmb->mat[i]->id.us++;
-                                       }
-                               }
-                       }
-                       break;
-#endif
-
-               case OB_MESH:
-                       if (!cage) {
-                               Mesh *origmesh = ob->data;
-                               tmpmesh->flag = origmesh->flag;
-                               tmpmesh->mat = MEM_dupallocN(origmesh->mat);
-                               tmpmesh->totcol = origmesh->totcol;
-                               tmpmesh->smoothresh = origmesh->smoothresh;
-                               if (origmesh->mat) {
-                                       for (i = origmesh->totcol; i-- > 0; ) {
-                                               /* are we an object material or data based? */
-                                               tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : origmesh->mat[i];
-
-                                               if (tmpmesh->mat[i]) {
-                                                       tmpmesh->mat[i]->id.us++;
-                                               }
-                                       }
-                               }
-                       }
-                       break;
-       } /* end copy materials */
-
-       if (calc_tessface) {
-               /* cycles and exporters rely on this still */
-               BKE_mesh_tessface_ensure(tmpmesh);
-       }
-
-       /* make sure materials get updated in objects */
-       test_object_materials(bmain, &tmpmesh->id);
-
-       return tmpmesh;
+       return BKE_mesh_new_from_object(bmain, sce, ob, apply_modifiers, settings, calc_tessface, calc_undeformed);
 }
 
 static void rna_Main_meshes_remove(Main *bmain, ReportList *reports, PointerRNA *mesh_ptr)
index 1ec11c280cb2fe8f2b40de522f4c4145133b2feb..16cc82bbca1365d65d163fb238f97c6ea237dc1e 100644 (file)
 
 #include "RE_engine.h"
 #include "RE_pipeline.h"
+#include "RE_engine.h"
+
 
+EnumPropertyItem render_pass_type_items[] = {
+       {SCE_PASS_COMBINED, "COMBINED", 0, "Combined", ""},
+       {SCE_PASS_Z, "Z", 0, "Z", ""},
+       {SCE_PASS_RGBA, "COLOR", 0, "Color", ""},
+       {SCE_PASS_DIFFUSE, "DIFFUSE", 0, "Diffuse", ""},
+       {SCE_PASS_SPEC, "SPECULAR", 0, "Specular", ""},
+       {SCE_PASS_SHADOW, "SHADOW", 0, "Shadow", ""},
+       {SCE_PASS_AO, "AO", 0, "AO", ""},
+       {SCE_PASS_REFLECT, "REFLECTION", 0, "Reflection", ""},
+       {SCE_PASS_NORMAL, "NORMAL", 0, "Normal", ""},
+       {SCE_PASS_VECTOR, "VECTOR", 0, "Vector", ""},
+       {SCE_PASS_REFRACT, "REFRACTION", 0, "Refraction", ""},
+       {SCE_PASS_INDEXOB, "OBJECT_INDEX", 0, "Object Index", ""},
+       {SCE_PASS_UV, "UV", 0, "UV", ""},
+       {SCE_PASS_MIST, "MIST", 0, "Mist", ""},
+       {SCE_PASS_EMIT, "EMIT", 0, "Emit", ""},
+       {SCE_PASS_ENVIRONMENT, "ENVIRONMENT", 0, "Environment", ""},
+       {SCE_PASS_INDEXMA, "MATERIAL_INDEX", 0, "Material Index", ""},
+       {SCE_PASS_DIFFUSE_DIRECT, "DIFFUSE_DIRECT", 0, "Diffuse Direct", ""},
+       {SCE_PASS_DIFFUSE_INDIRECT, "DIFFUSE_INDIRECT", 0, "Diffuse Indirect", ""},
+       {SCE_PASS_DIFFUSE_COLOR, "DIFFUSE_COLOR", 0, "Diffuse Color", ""},
+       {SCE_PASS_GLOSSY_DIRECT, "GLOSSY_DIRECT", 0, "Glossy Direct", ""},
+       {SCE_PASS_GLOSSY_INDIRECT, "GLOSSY_INDIRECT", 0, "Glossy Indirect", ""},
+       {SCE_PASS_GLOSSY_COLOR, "GLOSSY_COLOR", 0, "Glossy Color", ""},
+       {SCE_PASS_TRANSM_DIRECT, "TRANSMISSION_DIRECT", 0, "Transmission Direct", ""},
+       {SCE_PASS_TRANSM_INDIRECT, "TRANSMISSION_INDIRECT", 0, "Transmission Indirect", ""},
+       {SCE_PASS_TRANSM_COLOR, "TRANSMISSION_COLOR", 0, "Transmission Color", ""},
+       {SCE_PASS_SUBSURFACE_DIRECT, "SUBSURFACE_DIRECT", 0, "Subsurface Direct", ""},
+       {SCE_PASS_SUBSURFACE_INDIRECT, "SUBSURFACE_INDIRECT", 0, "Subsurface Indirect", ""},
+       {SCE_PASS_SUBSURFACE_COLOR, "SUBSURFACE_COLOR", 0, "Subsurface Color", ""},
+       {0, NULL, 0, NULL, NULL}
+};
 
 #ifdef RNA_RUNTIME
 
@@ -117,6 +151,30 @@ static void engine_render(RenderEngine *engine, struct Scene *scene)
        RNA_parameter_list_free(&list);
 }
 
+static void engine_bake(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)
+{
+       extern FunctionRNA rna_RenderEngine_bake_func;
+       PointerRNA ptr;
+       ParameterList list;
+       FunctionRNA *func;
+
+       RNA_pointer_create(NULL, engine->type->ext.srna, engine, &ptr);
+       func = &rna_RenderEngine_bake_func;
+
+       RNA_parameter_list_create(&list, &ptr, func);
+       RNA_parameter_set_lookup(&list, "scene", &scene);
+       RNA_parameter_set_lookup(&list, "object", &object);
+       RNA_parameter_set_lookup(&list, "pass_type", &pass_type);
+       RNA_parameter_set_lookup(&list, "pixel_array", &pixel_array);
+       RNA_parameter_set_lookup(&list, "num_pixels", &num_pixels);
+       RNA_parameter_set_lookup(&list, "depth", &depth);
+       RNA_parameter_set_lookup(&list, "result", &result);
+       engine->type->ext.call(NULL, &ptr, func, &list);
+
+       RNA_parameter_list_free(&list);
+}
+
 static void engine_view_update(RenderEngine *engine, const struct bContext *context)
 {
        extern FunctionRNA rna_RenderEngine_view_update_func;
@@ -189,7 +247,7 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, ReportList *reports, vo
        RenderEngineType *et, dummyet = {NULL};
        RenderEngine dummyengine = {NULL};
        PointerRNA dummyptr;
-       int have_function[5];
+       int have_function[6];
 
        /* setup dummy engine & engine type to store static properties in */
        dummyengine.type = &dummyet;
@@ -226,9 +284,10 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, ReportList *reports, vo
 
        et->update = (have_function[0]) ? engine_update : NULL;
        et->render = (have_function[1]) ? engine_render : NULL;
-       et->view_update = (have_function[2]) ? engine_view_update : NULL;
-       et->view_draw = (have_function[3]) ? engine_view_draw : NULL;
-       et->update_script_node = (have_function[4]) ? engine_update_script_node : NULL;
+       et->bake = (have_function[2]) ? engine_bake : NULL;
+       et->view_update = (have_function[3]) ? engine_view_update : NULL;
+       et->view_draw = (have_function[4]) ? engine_view_draw : NULL;
+       et->update_script_node = (have_function[5]) ? engine_update_script_node : NULL;
 
        BLI_addtail(&R_engines, et);
 
@@ -317,6 +376,12 @@ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values)
        memcpy(rpass->rect, values, sizeof(float) * rpass->rectx * rpass->recty * rpass->channels);
 }
 
+static PointerRNA rna_BakePixel_next_get(PointerRNA *ptr)
+{
+       BakePixel *bp = ptr->data;
+       return rna_pointer_inherit_refine(ptr, &RNA_BakePixel, bp + 1);
+}
+
 #else /* RNA_RUNTIME */
 
 static void rna_def_render_engine(BlenderRNA *brna)
@@ -344,6 +409,25 @@ static void rna_def_render_engine(BlenderRNA *brna)
        RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
        RNA_def_pointer(func, "scene", "Scene", "", "");
 
+       func = RNA_def_function(srna, "bake", NULL);
+       RNA_def_function_ui_description(func, "Bake passes");
+       RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
+       prop = RNA_def_pointer(func, "scene", "Scene", "", "");
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+       prop = RNA_def_pointer(func, "object", "Object", "", "");
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+       prop = RNA_def_enum(func, "pass_type", render_pass_type_items, 0, "Pass", "Pass to bake");
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+       prop = RNA_def_pointer(func, "pixel_array", "BakePixel", "", "");
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+       prop = RNA_def_int(func, "num_pixels", 0, 0, INT_MAX, "Number of Pixels", "Size of the baking batch", 0, INT_MAX);
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+       prop = RNA_def_int(func, "depth", 0, 0, INT_MAX, "Pixels depth", "Number of channels", 1, INT_MAX);
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+       /* TODO, see how array size of 0 works, this shouldnt be used */
+       prop = RNA_def_pointer(func, "result", "AnyType", "", "");
+       RNA_def_property_flag(prop, PROP_REQUIRED);
+
        /* viewport render callbacks */
        func = RNA_def_function(srna, "view_update", NULL);
        RNA_def_function_ui_description(func, "Update on data changes for viewport render");
@@ -588,39 +672,6 @@ static void rna_def_render_pass(BlenderRNA *brna)
        StructRNA *srna;
        PropertyRNA *prop;
 
-       static EnumPropertyItem pass_type_items[] = {
-               {SCE_PASS_COMBINED, "COMBINED", 0, "Combined", ""},
-               {SCE_PASS_Z, "Z", 0, "Z", ""},
-               {SCE_PASS_RGBA, "COLOR", 0, "Color", ""},
-               {SCE_PASS_DIFFUSE, "DIFFUSE", 0, "Diffuse", ""},
-               {SCE_PASS_SPEC, "SPECULAR", 0, "Specular", ""},
-               {SCE_PASS_SHADOW, "SHADOW", 0, "Shadow", ""},
-               {SCE_PASS_AO, "AO", 0, "AO", ""},
-               {SCE_PASS_REFLECT, "REFLECTION", 0, "Reflection", ""},
-               {SCE_PASS_NORMAL, "NORMAL", 0, "Normal", ""},
-               {SCE_PASS_VECTOR, "VECTOR", 0, "Vector", ""},
-               {SCE_PASS_REFRACT, "REFRACTION", 0, "Refraction", ""},
-               {SCE_PASS_INDEXOB, "OBJECT_INDEX", 0, "Object Index", ""},
-               {SCE_PASS_UV, "UV", 0, "UV", ""},
-               {SCE_PASS_MIST, "MIST", 0, "Mist", ""},
-               {SCE_PASS_EMIT, "EMIT", 0, "Emit", ""},
-               {SCE_PASS_ENVIRONMENT, "ENVIRONMENT", 0, "Environment", ""},
-               {SCE_PASS_INDEXMA, "MATERIAL_INDEX", 0, "Material Index", ""},
-               {SCE_PASS_DIFFUSE_DIRECT, "DIFFUSE_DIRECT", 0, "Diffuse Direct", ""},
-               {SCE_PASS_DIFFUSE_INDIRECT, "DIFFUSE_INDIRECT", 0, "Diffuse Indirect", ""},
-               {SCE_PASS_DIFFUSE_COLOR, "DIFFUSE_COLOR", 0, "Diffuse Color", ""},
-               {SCE_PASS_GLOSSY_DIRECT, "GLOSSY_DIRECT", 0, "Glossy Direct", ""},
-               {SCE_PASS_GLOSSY_INDIRECT, "GLOSSY_INDIRECT", 0, "Glossy Indirect", ""},
-               {SCE_PASS_GLOSSY_COLOR, "GLOSSY_COLOR", 0, "Glossy Color", ""},
-               {SCE_PASS_TRANSM_DIRECT, "TRANSMISSION_DIRECT", 0, "Transmission Direct", ""},
-               {SCE_PASS_TRANSM_INDIRECT, "TRANSMISSION_INDIRECT", 0, "Transmission Indirect", ""},
-               {SCE_PASS_TRANSM_COLOR, "TRANSMISSION_COLOR", 0, "Transmission Color", ""},
-               {SCE_PASS_SUBSURFACE_DIRECT, "SUBSURFACE_DIRECT", 0, "Subsurface Direct", ""},
-               {SCE_PASS_SUBSURFACE_INDIRECT, "SUBSURFACE_INDIRECT", 0, "Subsurface Indirect", ""},
-               {SCE_PASS_SUBSURFACE_COLOR, "SUBSURFACE_COLOR", 0, "Subsurface Color", ""},
-               {0, NULL, 0, NULL, NULL}
-       };
-       
        srna = RNA_def_struct(brna, "RenderPass", NULL);
        RNA_def_struct_ui_text(srna, "Render Pass", "");
 
@@ -641,7 +692,7 @@ static void rna_def_render_pass(BlenderRNA *brna)
 
        prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "passtype");
-       RNA_def_property_enum_items(prop, pass_type_items);
+       RNA_def_property_enum_items(prop, render_pass_type_items);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
 
        prop = RNA_def_property(srna, "rect", PROP_FLOAT, PROP_NONE);
@@ -653,12 +704,56 @@ static void rna_def_render_pass(BlenderRNA *brna)
        RNA_define_verify_sdna(1);
 }
 
+static void rna_def_render_bake_pixel(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna = RNA_def_struct(brna, "BakePixel", NULL);
+       RNA_def_struct_ui_text(srna, "Bake Pixel", "");
+
+       RNA_define_verify_sdna(0);
+
+       prop = RNA_def_property(srna, "primitive_id", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "primitive_id");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       prop = RNA_def_property(srna, "uv", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_array(prop, 2);
+       RNA_def_property_float_sdna(prop, NULL, "uv");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       prop = RNA_def_property(srna, "du_dx", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "du_dx");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       prop = RNA_def_property(srna, "du_dy", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "du_dy");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       prop = RNA_def_property(srna, "dv_dx", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "dv_dx");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       prop = RNA_def_property(srna, "dv_dy", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "dv_dy");
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       prop = RNA_def_property(srna, "next", PROP_POINTER, PROP_NONE);
+       RNA_def_property_struct_type(prop, "BakePixel");
+       RNA_def_property_pointer_funcs(prop, "rna_BakePixel_next_get", NULL, NULL, NULL);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       RNA_define_verify_sdna(1);
+}
+
 void RNA_def_render(BlenderRNA *brna)
 {
        rna_def_render_engine(brna);
        rna_def_render_result(brna);
        rna_def_render_layer(brna);
        rna_def_render_pass(brna);
+       rna_def_render_bake_pixel(brna);
 }
 
 #endif /* RNA_RUNTIME */
index 37e279f143663fc0bfade82d2fc807d64e5b37eb..94cb1941a4fa5109cb48594b3114430fe65dc4d8 100644 (file)
 
 #include "BLI_threads.h"
 
+#ifdef WITH_OPENEXR
+EnumPropertyItem exr_codec_items[] = {
+       {R_IMF_EXR_CODEC_NONE, "NONE", 0, "None", ""},
+       {R_IMF_EXR_CODEC_PXR24, "PXR24", 0, "Pxr24 (lossy)", ""},
+       {R_IMF_EXR_CODEC_ZIP, "ZIP", 0, "ZIP (lossless)", ""},
+       {R_IMF_EXR_CODEC_PIZ, "PIZ", 0, "PIZ (lossless)", ""},
+       {R_IMF_EXR_CODEC_RLE, "RLE", 0, "RLE (lossless)", ""},
+       {0, NULL, 0, NULL, NULL}
+};
+#endif
+
 EnumPropertyItem uv_sculpt_relaxation_items[] = {
        {UV_SCULPT_TOOL_RELAX_LAPLACIAN, "LAPLACIAN", 0, "Laplacian", "Use Laplacian method for relaxation"},
        {UV_SCULPT_TOOL_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
@@ -295,6 +306,28 @@ EnumPropertyItem image_color_depth_items[] = {
        {0, NULL, 0, NULL, NULL}
 };
 
+EnumPropertyItem normal_space_items[] = {
+       {R_BAKE_SPACE_OBJECT, "OBJECT", 0, "Object", "Bake the normals in object space"},
+       {R_BAKE_SPACE_TANGENT, "TANGENT", 0, "Tangent", "Bake the normals in tangent space"},
+       {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem normal_swizzle_items[] = {
+       {R_BAKE_POSX, "POS_X", 0, "+X", ""},
+       {R_BAKE_POSY, "POS_Y", 0, "+Y", ""},
+       {R_BAKE_POSZ, "POS_Z", 0, "+Z", ""},
+       {R_BAKE_NEGX, "NEG_X", 0, "-X", ""},
+       {R_BAKE_NEGY, "NEG_Y", 0, "-Y", ""},
+       {R_BAKE_NEGZ, "NEG_Z", 0, "-Z", ""},
+       {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem bake_save_mode_items[] = {
+       {R_BAKE_SAVE_INTERNAL, "INTERNAL", 0, "Internal", "Save the baking map in an internal image datablock"},
+       {R_BAKE_SAVE_EXTERNAL, "EXTERNAL", 0, "External", "Save the baking map in an external file"},
+       {0, NULL, 0, NULL, NULL}
+};
+
 #ifdef RNA_RUNTIME
 
 #include "DNA_anim_types.h"
@@ -3062,6 +3095,110 @@ static void rna_def_scene_game_recast_data(BlenderRNA *brna)
        RNA_def_property_update(prop, NC_SCENE, NULL);
 }
 
+
+static void rna_def_bake_data(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna = RNA_def_struct(brna, "BakeSettings", NULL);
+       RNA_def_struct_sdna(srna, "BakeData");
+       RNA_def_struct_nested(brna, srna, "RenderSettings");
+       RNA_def_struct_ui_text(srna, "Bake Data", "Bake data for a Scene datablock");
+
+       prop = RNA_def_property(srna, "cage", PROP_STRING, PROP_NONE);
+       RNA_def_property_ui_text(prop, "Cage", "Object to use as cage");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+       RNA_def_property_ui_text(prop, "File Path", "Image filepath to use when saving externally");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "width", PROP_INT, PROP_PIXEL);
+       RNA_def_property_range(prop, 4, 10000);
+       RNA_def_property_ui_text(prop, "Width", "Horizontal dimension of the baking map");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "height", PROP_INT, PROP_PIXEL);
+       RNA_def_property_range(prop, 4, 10000);
+       RNA_def_property_ui_text(prop, "Height", "Vertical dimension of the baking map");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "margin", PROP_INT, PROP_PIXEL);
+       RNA_def_property_range(prop, 0, SHRT_MAX);
+       RNA_def_property_ui_range(prop, 0, 64, 1, 1);
+       RNA_def_property_ui_text(prop, "Margin", "Extends the baked result as a post process filter");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "cage_extrusion", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0, MAXFLOAT);
+       RNA_def_property_ui_range(prop, 0.0, 1.0, 1, 3);
+       RNA_def_property_ui_text(prop, "Cage Extrusion",
+                                "Distance to use for the inward ray cast when using selected to active");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "normal_space", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "normal_space");
+       RNA_def_property_enum_items(prop, normal_space_items);
+       RNA_def_property_ui_text(prop, "Normal Space", "Choose normal space for baking");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "normal_r", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "normal_swizzle[0]");
+       RNA_def_property_enum_items(prop, normal_swizzle_items);
+       RNA_def_property_ui_text(prop, "Normal Space", "Axis to bake in red channel");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "normal_g", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "normal_swizzle[1]");
+       RNA_def_property_enum_items(prop, normal_swizzle_items);
+       RNA_def_property_ui_text(prop, "Normal Space", "Axis to bake in green channel");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "normal_b", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "normal_swizzle[2]");
+       RNA_def_property_enum_items(prop, normal_swizzle_items);
+       RNA_def_property_ui_text(prop, "Normal Space", "Axis to bake in blue channel");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "image_settings", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_pointer_sdna(prop, NULL, "im_format");
+       RNA_def_property_struct_type(prop, "ImageFormatSettings");
+       RNA_def_property_ui_text(prop, "Image Format", "");
+
+       prop = RNA_def_property(srna, "save_mode", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_bitflag_sdna(prop, NULL, "save_mode");
+       RNA_def_property_enum_items(prop, bake_save_mode_items);
+       RNA_def_property_ui_text(prop, "Save Mode", "Choose how to save the baking map");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       /* flags */
+       prop = RNA_def_property(srna, "use_selected_to_active", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", R_BAKE_TO_ACTIVE);
+       RNA_def_property_ui_text(prop, "Selected to Active",
+                                "Bake shading on the surface of selected objects to the active object");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "use_clear", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", R_BAKE_CLEAR);
+       RNA_def_property_ui_text(prop, "Clear",
+                                "Clear Images before baking (internal only)");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "use_split_materials", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", R_BAKE_SPLIT_MAT);
+       RNA_def_property_ui_text(prop, "Split Materials",
+                                "Split external images per material (external only)");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+       prop = RNA_def_property(srna, "use_automatic_name", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", R_BAKE_AUTO_NAME);
+       RNA_def_property_ui_text(prop, "Automatic Name",
+                                "Automatically name the output file with the pass type (external only)");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+}
+
 static void rna_def_scene_game_data(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -3591,16 +3728,6 @@ static void rna_def_render_layers(BlenderRNA *brna, PropertyRNA *cprop)
 
 static void rna_def_scene_image_format_data(BlenderRNA *brna)
 {
-#ifdef WITH_OPENEXR
-       static EnumPropertyItem exr_codec_items[] = {
-               {R_IMF_EXR_CODEC_NONE, "NONE", 0, "None", ""},
-               {R_IMF_EXR_CODEC_PXR24, "PXR24", 0, "Pxr24 (lossy)", ""},
-               {R_IMF_EXR_CODEC_ZIP, "ZIP", 0, "ZIP (lossless)", ""},
-               {R_IMF_EXR_CODEC_PIZ, "PIZ", 0, "PIZ (lossless)", ""},
-               {R_IMF_EXR_CODEC_RLE, "RLE", 0, "RLE (lossless)", ""},
-               {0, NULL, 0, NULL, NULL}
-       };
-#endif
 
 #ifdef WITH_OPENJPEG
        static EnumPropertyItem jp2_codec_items[] = {
@@ -4872,6 +4999,21 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
        RNA_def_property_range(prop, 0.f, 10000.f);
        RNA_def_property_ui_text(prop, "Line Thickness", "Line thickness in pixels");
 
+       /* Bake Settings */
+       prop = RNA_def_property(srna, "bake", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_pointer_sdna(prop, NULL, "bake");
+       RNA_def_property_struct_type(prop, "BakeSettings");
+       RNA_def_property_ui_text(prop, "Bake Data", "");
+
+       /* Nestled Data  */
+       /* *** Non-Animated *** */
+       RNA_define_animate_sdna(false);
+       rna_def_bake_data(brna);
+       RNA_define_animate_sdna(true);
+
+       /* *** Animated *** */
+
        /* Scene API */
        RNA_api_scene_render(srna);
 }
index b56c209ca4a66dc433017ede69ef1169c75ccd25..8e326e770fc59c0761958e41f9ba7a123ee1ac3f 100644 (file)
@@ -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 (file)
index 0000000..d59819c
--- /dev/null
@@ -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__ */
index bd976e6c14a528cd503e267e47c91c57ccd6ec4c..2c6492b5c5a6349c6e5c2042618d6a8f79d95bf9 100644 (file)
@@ -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__ */
 
index f3cddf0c7cb466b9af5660e9320f76dff8baae46..f2793a9bc5bc4a3085c19592ef7f3ef00d54316d 100644 (file)
@@ -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 (file)
index 0000000..7ad07e2
--- /dev/null
@@ -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;
+               }
+       }
+}
index f5901370c6105184448adda0acca27ae60bf9604..e8751210540f8e30db1a7aa5a51689b5f2cebe33 100644 (file)
@@ -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)
index dd00e691f5a6051923c9a0f60b68cf4d96be8097..ba54357751afb568ee63dc86c8186161e9222477 100644 (file)
@@ -374,6 +374,7 @@ enum {
        WM_JOB_TYPE_COMPOSITE,
        WM_JOB_TYPE_RENDER,
        WM_JOB_TYPE_RENDER_PREVIEW,  /* UI preview */
+       WM_JOB_TYPE_RENDER_BAKE,
        WM_JOB_TYPE_SCREENCAST,
        WM_JOB_TYPE_OBJECT_SIM_OCEAN,
        WM_JOB_TYPE_OBJECT_SIM_FLUID,