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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_mesh.h3
-rw-r--r--source/blender/blenkernel/intern/mesh.c219
-rw-r--r--source/blender/blenkernel/intern/scene.c17
-rw-r--r--source/blender/blenloader/intern/versioning_270.c25
-rw-r--r--source/blender/editors/object/CMakeLists.txt1
-rw-r--r--source/blender/editors/object/object_bake.c2
-rw-r--r--source/blender/editors/object/object_bake_api.c1082
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/makesdna/DNA_scene_types.h105
-rw-r--r--source/blender/makesrna/RNA_enum_types.h9
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c199
-rw-r--r--source/blender/makesrna/intern/rna_render.c171
-rw-r--r--source/blender/makesrna/intern/rna_scene.c162
-rw-r--r--source/blender/render/CMakeLists.txt2
-rw-r--r--source/blender/render/extern/include/RE_bake.h105
-rw-r--r--source/blender/render/extern/include/RE_engine.h4
-rw-r--r--source/blender/render/intern/source/bake.c2
-rw-r--r--source/blender/render/intern/source/bake_api.c829
-rw-r--r--source/blender/render/intern/source/external_engine.c83
-rw-r--r--source/blender/windowmanager/WM_api.h1
21 files changed, 2743 insertions, 280 deletions
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 4b346b201ba..587dea5095c 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -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]);
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index f2ff350178f..7b415cff555 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -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;
+}
+
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 1e7b0d343ba..0e95cf1d418 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -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;
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 0fe105346a3..a6cd854d874 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -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;
+ }
+ }
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 8277c7f7b86..1bb35b65918 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -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
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index 2699d28a10a..94574e81b81 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -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
index 00000000000..813e28b25ed
--- /dev/null
+++ b/source/blender/editors/object/object_bake_api.c
@@ -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");
+}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 5864f1e618e..fd6b9a1bad0 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -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);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 119107a0704..a8f07747d3a 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -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);
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 5a47e76b4f9..ceb938c3af2 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -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
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index 0b73fc932a9..3d4ed0d6f51 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -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[];
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 550fe4b0baa..bac1f132126 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -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)
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 1ec11c280cb..16cc82bbca1 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -38,7 +38,41 @@
#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 */
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 37e279f1436..94cb1941a4f 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -75,6 +75,17 @@
#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);
}
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index b56c209ca4a..8e326e770fc 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -53,6 +53,7 @@ set(SRC
intern/raytrace/rayobject_rtbuild.cpp
intern/raytrace/rayobject_vbvh.cpp
intern/source/bake.c
+ intern/source/bake_api.c
intern/source/convertblender.c
intern/source/envmap.c
intern/source/external_engine.c
@@ -82,6 +83,7 @@ set(SRC
intern/source/zbuf.c
extern/include/RE_engine.h
+ extern/include/RE_bake.h
extern/include/RE_multires_bake.h
extern/include/RE_pipeline.h
extern/include/RE_render_ext.h
diff --git a/source/blender/render/extern/include/RE_bake.h b/source/blender/render/extern/include/RE_bake.h
new file mode 100644
index 00000000000..d59819c8ef4
--- /dev/null
+++ b/source/blender/render/extern/include/RE_bake.h
@@ -0,0 +1,105 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2010 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s):
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file RE_bake.h
+ * \ingroup render
+ */
+
+#ifndef __RE_BAKE_H__
+#define __RE_BAKE_H__
+
+struct Render;
+struct Mesh;
+
+typedef struct BakeImage {
+ struct Image *image;
+ int width;
+ int height;
+ int offset;
+} BakeImage;
+
+typedef struct BakeImages {
+ BakeImage *data; /* all the images of an object */
+ int *lookup; /* lookup table from Material to BakeImage */
+ int size;
+} BakeImages;
+
+typedef struct BakePixel {
+ int primitive_id;
+ float uv[2];
+ float du_dx, du_dy;
+ float dv_dx, dv_dy;
+} BakePixel;
+
+typedef struct BakeHighPolyData {
+ struct BakePixel *pixel_array;
+ struct Object *ob;
+ struct ModifierData *tri_mod;
+ struct Mesh *me;
+ char restrict_flag;
+ float mat_lowtohigh[4][4];
+} BakeHighPolyData;
+
+/* external_engine.c */
+bool RE_bake_has_engine(struct Render *re);
+
+bool RE_bake_engine(
+ struct Render *re, struct Object *object, const BakePixel pixel_array[],
+ const int num_pixels, const int depth, const ScenePassType pass_type, float result[]);
+
+/* bake.c */
+int RE_pass_depth(const ScenePassType pass_type);
+bool RE_bake_internal(
+ struct Render *re, struct Object *object, const BakePixel pixel_array[],
+ const int num_pixels, const int depth, const ScenePassType pass_type, float result[]);
+
+void RE_bake_pixels_populate_from_objects(
+ struct Mesh *me_low, BakePixel pixel_array_from[],
+ BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels,
+ const float cage_extrusion);
+
+void RE_bake_pixels_populate(
+ struct Mesh *me, struct BakePixel *pixel_array,
+ const int num_pixels, const struct BakeImages *bake_images);
+
+void RE_bake_mask_fill(const BakePixel pixel_array[], const int num_pixels, char *mask);
+
+void RE_bake_margin(struct ImBuf *ibuf, char *mask, const int margin);
+
+void RE_bake_normal_world_to_object(
+ const BakePixel pixel_array[], const int num_pixels, const int depth, float result[],
+ struct Object *ob, const BakeNormalSwizzle normal_swizzle[3]);
+void RE_bake_normal_world_to_tangent(
+ const BakePixel pixel_array[], const int num_pixels, const int depth, float result[],
+ struct Mesh *me, const BakeNormalSwizzle normal_swizzle[3]);
+void RE_bake_normal_world_to_world(
+ const BakePixel pixel_array[], const int num_pixels, const int depth, float result[],
+ const BakeNormalSwizzle normal_swizzle[3]);
+
+void RE_bake_ibuf_clear(struct BakeImages *bake_images, const bool is_tangent);
+
+#endif /* __RE_BAKE_H__ */
diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h
index bd976e6c14a..2c6492b5c5a 100644
--- a/source/blender/render/extern/include/RE_engine.h
+++ b/source/blender/render/extern/include/RE_engine.h
@@ -35,6 +35,7 @@
#include "DNA_listBase.h"
#include "DNA_scene_types.h"
#include "RNA_types.h"
+#include "RE_bake.h"
struct bNode;
struct bNodeTree;
@@ -47,6 +48,7 @@ struct RenderLayer;
struct RenderResult;
struct ReportList;
struct Scene;
+struct BakePixel;
/* External Engine */
@@ -85,6 +87,7 @@ typedef struct RenderEngineType {
void (*update)(struct RenderEngine *engine, struct Main *bmain, struct Scene *scene);
void (*render)(struct RenderEngine *engine, struct Scene *scene);
+ void (*bake)(struct RenderEngine *engine, struct Scene *scene, struct Object *object, const int pass_type, const struct BakePixel *pixel_array, const int num_pixels, const int depth, void *result);
void (*view_update)(struct RenderEngine *engine, const struct bContext *context);
void (*view_draw)(struct RenderEngine *engine, const struct bContext *context);
@@ -153,6 +156,7 @@ RenderEngineType *RE_engines_find(const char *idname);
void RE_engine_get_current_tiles(struct Render *re, int *total_tiles_r, rcti **tiles_r);
struct RenderData *RE_engine_get_render_data(struct Render *re);
+void RE_bake_engine_set_engine_parameters(struct Render *re, struct Main *bmain, struct Scene *scene);
#endif /* __RE_ENGINE_H__ */
diff --git a/source/blender/render/intern/source/bake.c b/source/blender/render/intern/source/bake.c
index f3cddf0c7cb..f2793a9bc5b 100644
--- a/source/blender/render/intern/source/bake.c
+++ b/source/blender/render/intern/source/bake.c
@@ -54,6 +54,8 @@
#include "IMB_imbuf.h"
#include "IMB_colormanagement.h"
+#include "RE_bake.h"
+
/* local include */
#include "rayintersection.h"
#include "rayobject.h"
diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c
new file mode 100644
index 00000000000..7ad07e2aaf4
--- /dev/null
+++ b/source/blender/render/intern/source/bake_api.c
@@ -0,0 +1,829 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/source/bake_api.c
+ * \ingroup render
+ *
+ * \brief The API itself is simple. Blender sends a populated array of BakePixels to the renderer, and gets back an
+ * array of floats with the result.
+ *
+ * \section bake_api Development Notes for External Engines
+ *
+ * The Bake API is fully implemented with Python rna functions. The operator expects/call a function:
+ *
+ * def bake(scene, object, pass_type, pixel_array, num_pixels, depth, result)
+ * - scene: current scene (Python object)
+ * - object: object to render (Python object)
+ * - pass_type: pass to render (string, e.g., "COMBINED", "AO", "NORMAL", ...)
+ * - pixel_array: list of primitive ids and barycentric coordinates to bake(Python object, see bake_pixel)
+ * - num_pixels: size of pixel_array, number of pixels to bake (int)
+ * - depth: depth of pixels to return (int, assuming always 4 now)
+ * - result: array to be populated by the engine (float array, PyLong_AsVoidPtr)
+ *
+ * \note Normals are expected to be in World Space and in the +X, +Y, +Z orientation.
+ *
+ * \subsection bake_pixel BakePixel data structure
+ *
+ * pixel_array is a Python object storing BakePixel elements:
+
+ * struct BakePixel {
+ * int primitive_id;
+ * float u, v;
+ * float dudx, dudy;
+ * float dvdx, dvdy;
+ * };
+ *
+ * In python you have access to:
+ * - primitive_id, u, v, du_dx, du_dy, next
+ * - next() is a function that returns the next BakePixel in the array.
+ *
+ * \note Pixels that should not be baked have primitive_id = -1
+ *
+ * For a complete implementation example look at the Cycles Bake commit.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "DNA_mesh_types.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_image.h"
+#include "BKE_node.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "RE_bake.h"
+
+/* local include */
+#include "render_types.h"
+#include "zbuf.h"
+
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
+/* only to be used here in this file, it's for speed */
+extern struct Render R;
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+
+typedef struct BakeDataZSpan {
+ BakePixel *pixel_array;
+ int primitive_id;
+ BakeImage *bk_image;
+ ZSpan *zspan;
+} BakeDataZSpan;
+
+/**
+ * struct wrapping up tangent space data
+ */
+typedef struct TSpace {
+ float tangent[3];
+ float sign;
+} TSpace;
+
+typedef struct TriTessFace {
+ const MVert *mverts[3];
+ const TSpace *tspace[3];
+ float normal[3]; /* for flat faces */
+ bool is_smooth;
+} TriTessFace;
+
+static void store_bake_pixel(void *handle, int x, int y, float u, float v)
+{
+ BakeDataZSpan *bd = (BakeDataZSpan *)handle;
+ BakePixel *pixel;
+
+ const int width = bd->bk_image->width;
+ const int offset = bd->bk_image->offset;
+ const int i = offset + y * width + x;
+
+ pixel = &bd->pixel_array[i];
+ pixel->primitive_id = bd->primitive_id;
+
+ copy_v2_fl2(pixel->uv, u, v);
+
+ pixel->du_dx =
+ pixel->du_dy =
+ pixel->dv_dx =
+ pixel->dv_dy =
+ 0.0f;
+}
+
+void RE_bake_mask_fill(const BakePixel pixel_array[], const int num_pixels, char *mask)
+{
+ int i;
+ if (!mask)
+ return;
+
+ /* only extend to pixels outside the mask area */
+ for (i = 0; i < num_pixels; i++) {
+ if (pixel_array[i].primitive_id != -1) {
+ mask[i] = FILTER_MASK_USED;
+ }
+ }
+}
+
+void RE_bake_margin(ImBuf *ibuf, char *mask, const int margin)
+{
+ /* margin */
+ IMB_filter_extend(ibuf, mask, margin);
+
+ if (ibuf->planes != R_IMF_PLANES_RGBA)
+ /* clear alpha added by filtering */
+ IMB_rectfill_alpha(ibuf, 1.0f);
+}
+
+/**
+ * This function returns the coordinate and normal of a barycentric u,v for a face defined by the primitive_id index.
+ */
+static void calc_point_from_barycentric(
+ TriTessFace *triangles, int primitive_id, float u, float v, float cage_extrusion,
+ float r_co[3], float r_dir[3])
+{
+ float data[3][3];
+ float coord[3];
+ float dir[3];
+ float cage[3];
+
+ TriTessFace *triangle = &triangles[primitive_id];
+
+ copy_v3_v3(data[0], triangle->mverts[0]->co);
+ copy_v3_v3(data[1], triangle->mverts[1]->co);
+ copy_v3_v3(data[2], triangle->mverts[2]->co);
+
+ interp_barycentric_tri_v3(data, u, v, coord);
+
+ normal_short_to_float_v3(data[0], triangle->mverts[0]->no);
+ normal_short_to_float_v3(data[1], triangle->mverts[1]->no);
+ normal_short_to_float_v3(data[2], triangle->mverts[2]->no);
+
+ interp_barycentric_tri_v3(data, u, v, dir);
+ normalize_v3_v3(cage, dir);
+ mul_v3_fl(cage, cage_extrusion);
+
+ add_v3_v3(coord, cage);
+
+ normalize_v3_v3(dir, dir);
+ mul_v3_fl(dir, -1.0f);
+
+ copy_v3_v3(r_co, coord);
+ copy_v3_v3(r_dir, dir);
+}
+
+/**
+ * This function returns the barycentric u,v of a face for a coordinate. The face is defined by its index.
+ */
+static void calc_barycentric_from_point(
+ TriTessFace *triangles, const int index, const float co[3],
+ int *r_primitive_id, float r_uv[2])
+{
+ TriTessFace *triangle = &triangles[index];
+ resolve_tri_uv_v3(r_uv, co,
+ triangle->mverts[0]->co,
+ triangle->mverts[1]->co,
+ triangle->mverts[2]->co);
+ *r_primitive_id = index;
+}
+
+/**
+ * This function populates pixel_array and returns TRUE if things are correct
+ */
+static bool cast_ray_highpoly(
+ BVHTreeFromMesh *treeData, TriTessFace *triangles[], BakeHighPolyData *highpoly,
+ float const co_low[3], const float dir[3], const int pixel_id, const int tot_highpoly)
+{
+ int i;
+ int primitive_id = -1;
+ float uv[2];
+ int hit_mesh = -1;
+ float hit_distance = FLT_MAX;
+
+ BVHTreeRayHit *hits;
+ hits = MEM_mallocN(sizeof(BVHTreeRayHit) * tot_highpoly, "Bake Highpoly to Lowpoly: BVH Rays");
+
+ for (i = 0; i < tot_highpoly; i++) {
+ float co_high[3];
+ hits[i].index = -1;
+ /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */
+ hits[i].dist = 10000.0f;
+
+ copy_v3_v3(co_high, co_low);
+
+ /* transform the ray from the lowpoly to the highpoly space */
+ mul_m4_v3(highpoly[i].mat_lowtohigh, co_high);
+
+ /* cast ray */
+ BLI_bvhtree_ray_cast(treeData[i].tree, co_high, dir, 0.0f, &hits[i], treeData[i].raycast_callback, &treeData[i]);
+
+ if (hits[i].index != -1) {
+ /* cull backface */
+ const float dot = dot_v3v3(dir, hits[i].no);
+ if (dot < 0.0f) {
+ if (hits[i].dist < hit_distance) {
+ hit_mesh = i;
+ hit_distance = hits[i].dist;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < tot_highpoly; i++) {
+ if (hit_mesh == i) {
+ calc_barycentric_from_point(triangles[i], hits[i].index, hits[i].co, &primitive_id, uv);
+ highpoly[i].pixel_array[pixel_id].primitive_id = primitive_id;
+ copy_v2_v2(highpoly[i].pixel_array[pixel_id].uv, uv);
+ }
+ else {
+ highpoly[i].pixel_array[pixel_id].primitive_id = -1;
+ }
+ }
+
+ MEM_freeN(hits);
+ return hit_mesh != -1;
+}
+
+/**
+ * This function populates an array of verts for the triangles of a mesh
+ * Tangent and Normals are also stored
+ */
+static void mesh_calc_tri_tessface(
+ TriTessFace *triangles, Mesh *me, bool tangent, DerivedMesh *dm)
+{
+ int i;
+ int p_id;
+ MFace *mface;
+ MVert *mvert;
+ TSpace *tspace;
+ float *precomputed_normals;
+ bool calculate_normal;
+
+ mface = CustomData_get_layer(&me->fdata, CD_MFACE);
+ mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
+
+ if (tangent) {
+ DM_ensure_normals(dm);
+ DM_add_tangent_layer(dm);
+
+ precomputed_normals = dm->getTessFaceDataArray(dm, CD_NORMAL);
+ calculate_normal = precomputed_normals ? false : true;
+
+ //mface = dm->getTessFaceArray(dm);
+ //mvert = dm->getVertArray(dm);
+
+ tspace = dm->getTessFaceDataArray(dm, CD_TANGENT);
+ BLI_assert(tspace);
+ }
+
+ p_id = -1;
+ for (i = 0; i < me->totface; i++) {
+ MFace *mf = &mface[i];
+ TSpace *ts = &tspace[i * 4];
+
+ p_id++;
+
+ triangles[p_id].mverts[0] = &mvert[mf->v1];
+ triangles[p_id].mverts[1] = &mvert[mf->v2];
+ triangles[p_id].mverts[2] = &mvert[mf->v3];
+ triangles[p_id].is_smooth = (mf->flag & ME_SMOOTH) != 0;
+
+ if (tangent) {
+ triangles[p_id].tspace[0] = &ts[0];
+ triangles[p_id].tspace[1] = &ts[1];
+ triangles[p_id].tspace[2] = &ts[2];
+
+ if (calculate_normal) {
+ if (mf->v4 != 0) {
+ normal_quad_v3(triangles[p_id].normal,
+ mvert[mf->v1].co,
+ mvert[mf->v2].co,
+ mvert[mf->v3].co,
+ mvert[mf->v4].co);
+ }
+ else {
+ normal_tri_v3(triangles[p_id].normal,
+ triangles[p_id].mverts[0]->co,
+ triangles[p_id].mverts[1]->co,
+ triangles[p_id].mverts[2]->co);
+ }
+ }
+ else {
+ copy_v3_v3(triangles[p_id].normal, &precomputed_normals[3 * i]);
+ }
+ }
+
+ /* 4 vertices in the face */
+ if (mf->v4 != 0) {
+ p_id++;
+
+ triangles[p_id].mverts[0] = &mvert[mf->v1];
+ triangles[p_id].mverts[1] = &mvert[mf->v3];
+ triangles[p_id].mverts[2] = &mvert[mf->v4];
+ triangles[p_id].is_smooth = (mf->flag & ME_SMOOTH) != 0;
+
+ if (tangent) {
+ triangles[p_id].tspace[0] = &ts[0];
+ triangles[p_id].tspace[1] = &ts[2];
+ triangles[p_id].tspace[2] = &ts[3];
+
+ /* same normal as the other "triangle" */
+ copy_v3_v3(triangles[p_id].normal, triangles[p_id - 1].normal);
+ }
+ }
+ }
+
+ BLI_assert(p_id < me->totface * 2);
+}
+
+void RE_bake_pixels_populate_from_objects(
+ struct Mesh *me_low, BakePixel pixel_array_from[],
+ BakeHighPolyData highpoly[], const int tot_highpoly, const int num_pixels,
+ const float cage_extrusion)
+{
+ int i;
+ int primitive_id;
+ float u, v;
+
+ DerivedMesh **dm_highpoly;
+ BVHTreeFromMesh *treeData;
+
+ /* Note: all coordinates are in local space */
+ TriTessFace *tris_low;
+ TriTessFace **tris_high;
+
+ /* assume all lowpoly tessfaces can be quads */
+ tris_low = MEM_callocN(sizeof(TriTessFace) * (me_low->totface * 2), "MVerts Lowpoly Mesh");
+ tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array");
+
+ /* assume all highpoly tessfaces are triangles */
+ dm_highpoly = MEM_callocN(sizeof(DerivedMesh *) * tot_highpoly, "Highpoly Derived Meshes");
+ treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees");
+
+ mesh_calc_tri_tessface(tris_low, me_low, false, NULL);
+
+ for (i = 0; i < tot_highpoly; i++) {
+ tris_high[i] = MEM_callocN(sizeof(TriTessFace) * highpoly[i].me->totface, "MVerts Highpoly Mesh");
+ mesh_calc_tri_tessface(tris_high[i], highpoly[i].me, false, NULL);
+
+ dm_highpoly[i] = CDDM_from_mesh(highpoly[i].me);
+
+ /* Create a bvh-tree for each highpoly object */
+ bvhtree_from_mesh_faces(&treeData[i], dm_highpoly[i], 0.0, 2, 6);
+
+ if (&treeData[i].tree == NULL) {
+ printf("Baking: Out of memory\n");
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; i < num_pixels; i++) {
+ float co[3];
+ float dir[3];
+
+ primitive_id = pixel_array_from[i].primitive_id;
+
+ if (primitive_id == -1) {
+ int j;
+ for (j = 0; j < tot_highpoly; j++) {
+ highpoly[j].pixel_array[i].primitive_id = -1;
+ }
+ continue;
+ }
+
+ u = pixel_array_from[i].uv[0];
+ v = pixel_array_from[i].uv[1];
+
+ /* calculate from low poly mesh cage */
+ calc_point_from_barycentric(tris_low, primitive_id, u, v, cage_extrusion, co, dir);
+
+ /* cast ray */
+ if (!cast_ray_highpoly(treeData, tris_high, highpoly, co, dir, i, tot_highpoly)) {
+ /* if it fails mask out the original pixel array */
+ pixel_array_from[i].primitive_id = -1;
+ }
+ }
+
+
+ /* garbage collection */
+cleanup:
+ for (i = 0; i < tot_highpoly; i++) {
+ free_bvhtree_from_mesh(&treeData[i]);
+ dm_highpoly[i]->release(dm_highpoly[i]);
+ MEM_freeN(tris_high[i]);
+ }
+
+ MEM_freeN(tris_low);
+ MEM_freeN(tris_high);
+ MEM_freeN(treeData);
+ MEM_freeN(dm_highpoly);
+}
+
+void RE_bake_pixels_populate(
+ Mesh *me, BakePixel pixel_array[],
+ const int num_pixels, const BakeImages *bake_images)
+{
+ BakeDataZSpan bd;
+ int i, a;
+ int p_id;
+
+ MTFace *mtface;
+ MFace *mface;
+
+ /* we can't bake in edit mode */
+ if (me->edit_btmesh)
+ return;
+
+ bd.pixel_array = pixel_array;
+ bd.zspan = MEM_callocN(sizeof(ZSpan) * bake_images->size, "bake zspan");
+
+ /* initialize all pixel arrays so we know which ones are 'blank' */
+ for (i = 0; i < num_pixels; i++) {
+ pixel_array[i].primitive_id = -1;
+ }
+
+ for (i = 0; i < bake_images->size; i++) {
+ zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height, R.clipcrop);
+ }
+
+ mtface = CustomData_get_layer(&me->fdata, CD_MTFACE);
+ mface = CustomData_get_layer(&me->fdata, CD_MFACE);
+
+ if (mtface == NULL)
+ return;
+
+ p_id = -1;
+ for (i = 0; i < me->totface; i++) {
+ float vec[4][2];
+ MTFace *mtf = &mtface[i];
+ MFace *mf = &mface[i];
+ int mat_nr = mf->mat_nr;
+ int image_id = bake_images->lookup[mat_nr];
+
+ bd.bk_image = &bake_images->data[image_id];
+ bd.primitive_id = ++p_id;
+
+ for (a = 0; a < 4; a++) {
+ /* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests
+ * where a pixel gets in between 2 faces or the middle of a quad,
+ * camera aligned quads also have this problem but they are less common.
+ * Add a small offset to the UVs, fixes bug #18685 - Campbell */
+ vec[a][0] = mtf->uv[a][0] * (float)bd.bk_image->width - (0.5f + 0.001f);
+ vec[a][1] = mtf->uv[a][1] * (float)bd.bk_image->height - (0.5f + 0.002f);
+ }
+
+ zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel);
+
+ /* 4 vertices in the face */
+ if (mf->v4 != 0) {
+ bd.primitive_id = ++p_id;
+ zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[2], vec[3], store_bake_pixel);
+ }
+ }
+
+ for (i = 0; i < bake_images->size; i++) {
+ zbuf_free_span(&bd.zspan[i]);
+ }
+ MEM_freeN(bd.zspan);
+}
+
+/* ******************** NORMALS ************************ */
+
+/**
+ * convert a normalized normal to the -1.0 1.0 range
+ * the input is expected to be POS_X, POS_Y, POS_Z
+ */
+static void normal_uncompress(float out[3], const float in[3])
+{
+ int i;
+ for (i = 0; i < 3; i++)
+ out[i] = 2.0f * in[i] - 1.0f;
+}
+
+static void normal_compress(float out[3], const float in[3], const BakeNormalSwizzle normal_swizzle[3])
+{
+ const int swizzle_index[6] = {
+ 0, /* R_BAKE_POSX */
+ 1, /* R_BAKE_POSY */
+ 2, /* R_BAKE_POSZ */
+ 0, /* R_BAKE_NEGX */
+ 1, /* R_BAKE_NEGY */
+ 2, /* R_BAKE_NEGZ */
+ };
+ const float swizzle_sign[6] = {
+ +1.0f, /* R_BAKE_POSX */
+ +1.0f, /* R_BAKE_POSY */
+ +1.0f, /* R_BAKE_POSZ */
+ -1.0f, /* R_BAKE_NEGX */
+ -1.0f, /* R_BAKE_NEGY */
+ -1.0f, /* R_BAKE_NEGZ */
+ };
+
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ int index;
+ float sign;
+
+ sign = swizzle_sign[normal_swizzle[i]];
+ index = swizzle_index[normal_swizzle[i]];
+
+ /*
+ * There is a small 1e-5f bias for precision issues. otherwise
+ * we randomly get 127 or 128 for neutral colors in tangent maps.
+ * we choose 128 because it is the convention flat color. *
+ */
+
+ out[i] = sign * in[index] / 2.0f + 0.5f + 1e-5f;
+ }
+}
+
+/**
+ * This function converts an object space normal map to a tangent space normal map for a given low poly mesh
+ */
+void RE_bake_normal_world_to_tangent(
+ const BakePixel pixel_array[], const int num_pixels, const int depth,
+ float result[], Mesh *me, const BakeNormalSwizzle normal_swizzle[3])
+{
+ int i;
+
+ TriTessFace *triangles;
+
+ DerivedMesh *dm = CDDM_from_mesh(me);
+
+ triangles = MEM_callocN(sizeof(TriTessFace) * (me->totface * 2), "MVerts Mesh");
+ mesh_calc_tri_tessface(triangles, me, true, dm);
+
+ BLI_assert(num_pixels >= 3);
+
+ for (i = 0; i < num_pixels; i++) {
+ TriTessFace *triangle;
+ float tangents[3][3];
+ float normals[3][3];
+ float signs[3];
+ int j;
+
+ float tangent[3];
+ float normal[3];
+ float binormal[3];
+ float sign;
+ float u, v, w;
+
+ float tsm[3][3]; /* tangent space matrix */
+ float itsm[3][3];
+
+ int offset;
+ float nor[3]; /* texture normal */
+
+ bool is_smooth;
+
+ int primitive_id = pixel_array[i].primitive_id;
+
+ offset = i * depth;
+
+ if (primitive_id == -1) {
+ copy_v3_fl3(&result[offset], 0.5f, 0.5f, 1.0f);
+ continue;
+ }
+
+ triangle = &triangles[primitive_id];
+ is_smooth = triangle->is_smooth;
+
+ for (j = 0; j < 3; j++) {
+ const TSpace *ts;
+
+ if (is_smooth)
+ normal_short_to_float_v3(normals[j], triangle->mverts[j]->no);
+ else
+ normal[j] = triangle->normal[j];
+
+ ts = triangle->tspace[j];
+ copy_v3_v3(tangents[j], ts->tangent);
+ signs[j] = ts->sign;
+ }
+
+ u = pixel_array[i].uv[0];
+ v = pixel_array[i].uv[1];
+ w = 1.0f - u - v;
+
+ /* normal */
+ if (is_smooth)
+ interp_barycentric_tri_v3(normals, u, v, normal);
+
+ /* tangent */
+ interp_barycentric_tri_v3(tangents, u, v, tangent);
+
+ /* sign */
+ /* The sign is the same at all face vertices for any non degenerate face.
+ * Just in case we clamp the interpolated value though. */
+ sign = (signs[0] * u + signs[1] * v + signs[2] * w) < 0 ? (-1.0f) : 1.0f;
+
+ /* binormal */
+ /* B = sign * cross(N, T) */
+ cross_v3_v3v3(binormal, normal, tangent);
+ mul_v3_fl(binormal, sign);
+
+ /* populate tangent space matrix */
+ copy_v3_v3(tsm[0], tangent);
+ copy_v3_v3(tsm[1], binormal);
+ copy_v3_v3(tsm[2], normal);
+
+ /* texture values */
+ normal_uncompress(nor, &result[offset]);
+
+ invert_m3_m3(itsm, tsm);
+ mul_m3_v3(itsm, nor);
+ normalize_v3(nor);
+
+ /* save back the values */
+ normal_compress(&result[offset], nor, normal_swizzle);
+ }
+
+ /* garbage collection */
+ MEM_freeN(triangles);
+
+ if (dm)
+ dm->release(dm);
+}
+
+void RE_bake_normal_world_to_object(
+ const BakePixel pixel_array[], const int num_pixels, const int depth,
+ float result[], struct Object *ob, const BakeNormalSwizzle normal_swizzle[3])
+{
+ int i;
+ float iobmat[4][4];
+
+ invert_m4_m4(iobmat, ob->obmat);
+
+ for (i = 0; i < num_pixels; i++) {
+ int offset;
+ float nor[3];
+
+ if (pixel_array[i].primitive_id == -1)
+ continue;
+
+ offset = i * depth;
+ normal_uncompress(nor, &result[offset]);
+
+ mul_m4_v3(iobmat, nor);
+ normalize_v3(nor);
+
+ /* save back the values */
+ normal_compress(&result[offset], nor, normal_swizzle);
+ }
+}
+
+void RE_bake_normal_world_to_world(
+ const BakePixel pixel_array[], const int num_pixels, const int depth,
+ float result[], const BakeNormalSwizzle normal_swizzle[3])
+{
+ int i;
+
+ for (i = 0; i < num_pixels; i++) {
+ int offset;
+ float nor[3];
+
+ if (pixel_array[i].primitive_id == -1)
+ continue;
+
+ offset = i * depth;
+ normal_uncompress(nor, &result[offset]);
+
+ /* save back the values */
+ normal_compress(&result[offset], nor, normal_swizzle);
+ }
+}
+
+void RE_bake_ibuf_clear(BakeImages *bake_images, const bool is_tangent)
+{
+ ImBuf *ibuf;
+ void *lock;
+ Image *image;
+ int i;
+
+ const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f};
+ const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f};
+
+ for (i = 0; i < bake_images->size; i ++) {
+ image = bake_images->data[i].image;
+
+ ibuf = BKE_image_acquire_ibuf(image, NULL, &lock);
+ BLI_assert(ibuf);
+
+ if (is_tangent)
+ IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
+ else
+ IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
+
+ BKE_image_release_ibuf(image, ibuf, lock);
+ }
+}
+
+/* ************************************************************* */
+
+/**
+ * not the real UV, but the internal per-face UV instead
+ * I'm using it to test if everything is correct */
+static bool bake_uv(const BakePixel pixel_array[], const int num_pixels, const int depth, float result[])
+{
+ int i;
+
+ for (i=0; i < num_pixels; i++) {
+ int offset = i * depth;
+ copy_v2_v2(&result[offset], pixel_array[i].uv);
+ }
+
+ return true;
+}
+
+bool RE_bake_internal(
+ Render *UNUSED(re), Object *UNUSED(object), const BakePixel pixel_array[],
+ const int num_pixels, const int depth, const ScenePassType pass_type, float result[])
+{
+ switch (pass_type) {
+ case SCE_PASS_UV:
+ {
+ return bake_uv(pixel_array, num_pixels, depth, result);
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+int RE_pass_depth(const ScenePassType pass_type)
+{
+ /* IMB_buffer_byte_from_float assumes 4 channels
+ * making it work for now - XXX */
+ return 4;
+
+ switch (pass_type) {
+ case SCE_PASS_Z:
+ case SCE_PASS_AO:
+ case SCE_PASS_MIST:
+ {
+ return 1;
+ }
+ case SCE_PASS_UV:
+ {
+ return 2;
+ }
+ case SCE_PASS_RGBA:
+ {
+ return 4;
+ }
+ case SCE_PASS_COMBINED:
+ case SCE_PASS_DIFFUSE:
+ case SCE_PASS_SPEC:
+ case SCE_PASS_SHADOW:
+ case SCE_PASS_REFLECT:
+ case SCE_PASS_NORMAL:
+ case SCE_PASS_VECTOR:
+ case SCE_PASS_REFRACT:
+ case SCE_PASS_INDEXOB: /* XXX double check */
+ case SCE_PASS_INDIRECT:
+ case SCE_PASS_RAYHITS: /* XXX double check */
+ case SCE_PASS_EMIT:
+ case SCE_PASS_ENVIRONMENT:
+ case SCE_PASS_INDEXMA:
+ case SCE_PASS_DIFFUSE_DIRECT:
+ case SCE_PASS_DIFFUSE_INDIRECT:
+ case SCE_PASS_DIFFUSE_COLOR:
+ case SCE_PASS_GLOSSY_DIRECT:
+ case SCE_PASS_GLOSSY_INDIRECT:
+ case SCE_PASS_GLOSSY_COLOR:
+ case SCE_PASS_TRANSM_DIRECT:
+ case SCE_PASS_TRANSM_INDIRECT:
+ case SCE_PASS_TRANSM_COLOR:
+ case SCE_PASS_SUBSURFACE_DIRECT:
+ case SCE_PASS_SUBSURFACE_INDIRECT:
+ case SCE_PASS_SUBSURFACE_COLOR:
+ default:
+ {
+ return 3;
+ }
+ }
+}
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index f5901370c61..e8751210540 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -57,6 +57,7 @@
#include "RE_engine.h"
#include "RE_pipeline.h"
+#include "RE_bake.h"
#include "initrender.h"
#include "render_types.h"
@@ -67,7 +68,7 @@
static RenderEngineType internal_render_type = {
NULL, NULL,
"BLENDER_RENDER", N_("Blender Render"), RE_INTERNAL,
- NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
{NULL, NULL, NULL}
};
@@ -76,7 +77,7 @@ static RenderEngineType internal_render_type = {
static RenderEngineType internal_game_type = {
NULL, NULL,
"BLENDER_GAME", N_("Blender Game"), RE_INTERNAL | RE_GAME,
- NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
{NULL, NULL, NULL}
};
@@ -402,6 +403,84 @@ RenderData *RE_engine_get_render_data(Render *re)
return &re->r;
}
+/* Bake */
+void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene)
+{
+ re->scene = scene;
+ re->main = bmain;
+ re->r = scene->r;
+
+ /* prevent crash when freeing the scene
+ but it potentially leaves unfreed memory blocks
+ not sure how to fix this yet -- dfelinto */
+ re->r.layers.first = re->r.layers.last = NULL;
+}
+
+bool RE_bake_has_engine(Render *re)
+{
+ RenderEngineType *type = RE_engines_find(re->r.engine);
+ return (bool)(type->bake);
+}
+
+bool RE_bake_engine(
+ Render *re, Object *object, const BakePixel pixel_array[],
+ const int num_pixels, const int depth,
+ const ScenePassType pass_type, float result[])
+{
+ RenderEngineType *type = RE_engines_find(re->r.engine);
+ RenderEngine *engine;
+ int persistent_data = re->r.mode & R_PERSISTENT_DATA;
+
+ /* set render info */
+ re->i.cfra = re->scene->r.cfra;
+ BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2);
+ re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0;
+
+ /* render */
+ engine = re->engine;
+
+ if (!engine) {
+ engine = RE_engine_create(type);
+ re->engine = engine;
+ }
+
+ engine->flag |= RE_ENGINE_RENDERING;
+
+ /* TODO: actually link to a parent which shouldn't happen */
+ engine->re = re;
+
+ engine->resolution_x = re->winx;
+ engine->resolution_y = re->winy;
+
+ RE_parts_init(re, false);
+ engine->tile_x = re->partx;
+ engine->tile_y = re->party;
+
+ /* update is only called so we create the engine.session */
+ if (type->update)
+ type->update(engine, re->main, re->scene);
+
+ if (type->bake)
+ type->bake(engine, re->scene, object, pass_type, pixel_array, num_pixels, depth, result);
+
+ engine->tile_x = 0;
+ engine->tile_y = 0;
+ engine->flag &= ~RE_ENGINE_RENDERING;
+
+ /* re->engine becomes zero if user changed active render engine during render */
+ if (!persistent_data || !re->engine) {
+ RE_engine_free(engine);
+ re->engine = NULL;
+ }
+
+ RE_parts_free(re);
+
+ if (BKE_reports_contain(re->reports, RPT_ERROR))
+ G.is_break = true;
+
+ return true;
+}
+
/* Render */
static bool render_layer_exclude_animated(Scene *scene, SceneRenderLayer *srl)
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index dd00e691f5a..ba54357751a 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -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,