diff options
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, |